lsp-server first commit
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include "./tsl_keywords.hpp"
|
||||
|
||||
namespace tsl
|
||||
{
|
||||
std::vector<KeywordInfo> TslKeywords::keywords_;
|
||||
std::unordered_set<std::string> TslKeywords::keyword_set_;
|
||||
bool TslKeywords::initialized_ = false;
|
||||
|
||||
const std::vector<KeywordInfo>& TslKeywords::GetAllKeywords()
|
||||
{
|
||||
InitKeywords();
|
||||
return keywords_;
|
||||
}
|
||||
|
||||
std::vector<KeywordInfo> TslKeywords::GetKeywordsByCategory(KeywordCategory category)
|
||||
{
|
||||
InitKeywords();
|
||||
std::vector<KeywordInfo> result;
|
||||
|
||||
for (const auto& keyword : keywords_)
|
||||
if (keyword.category == category)
|
||||
result.push_back(keyword);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<lsp::CompletionItem> TslKeywords::GetCompletionItems(const std::string& prefix)
|
||||
{
|
||||
InitKeywords();
|
||||
std::vector<lsp::CompletionItem> result;
|
||||
std::string lower_prefix = ToLowerCase(prefix);
|
||||
|
||||
for (const auto& keyword : keywords_)
|
||||
{
|
||||
if (prefix.empty() || StartsWith(ToLowerCase(keyword.keyword), lower_prefix))
|
||||
{
|
||||
lsp::CompletionItem item;
|
||||
item.label = keyword.keyword;
|
||||
item.kind = keyword.completion_kind;
|
||||
item.detail = keyword.description;
|
||||
item.insert_text = keyword.keyword;
|
||||
result.push_back(item);
|
||||
}
|
||||
}
|
||||
// 按字母顺序排序
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const lsp::CompletionItem& a, const lsp::CompletionItem& b) {
|
||||
return a.label < b.label;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
KeywordCategory TslKeywords::GetKeywordCategory(const std::string& word)
|
||||
{
|
||||
InitKeywords();
|
||||
std::string lower_word = ToLowerCase(word);
|
||||
|
||||
for (const auto& keyword : keywords_)
|
||||
{
|
||||
if (ToLowerCase(keyword.keyword) == lower_word) {
|
||||
return keyword.category;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到,返回一个默认值(可以考虑抛出异常)
|
||||
return KeywordCategory::kProgramStructure;
|
||||
}
|
||||
|
||||
void TslKeywords::InitKeywords()
|
||||
{
|
||||
if (initialized_)
|
||||
return;
|
||||
keywords_ = {
|
||||
// Program Structure
|
||||
{ "program", KeywordCategory::kProgramStructure, "Program declaration", lsp::CompletionItemKind::kKeyword },
|
||||
{ "function", KeywordCategory::kProgramStructure, "Function declaration", lsp::CompletionItemKind::kFunction },
|
||||
{ "procedure", KeywordCategory::kProgramStructure, "Procedure declaration", lsp::CompletionItemKind::kFunction },
|
||||
{ "unit", KeywordCategory::kProgramStructure, "Unit declaration", lsp::CompletionItemKind::kModule },
|
||||
{ "uses", KeywordCategory::kProgramStructure, "Uses clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "implementation", KeywordCategory::kProgramStructure, "Implementation section", lsp::CompletionItemKind::kKeyword },
|
||||
{ "interface", KeywordCategory::kProgramStructure, "Interface section", lsp::CompletionItemKind::kInterface },
|
||||
{ "initialization", KeywordCategory::kProgramStructure, "Initialization section", lsp::CompletionItemKind::kKeyword },
|
||||
{ "finalization", KeywordCategory::kProgramStructure, "Finalization section", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Data Types
|
||||
{ "string", KeywordCategory::kDataTypes, "String data type", lsp::CompletionItemKind::kClass },
|
||||
{ "integer", KeywordCategory::kDataTypes, "Integer data type", lsp::CompletionItemKind::kClass },
|
||||
{ "boolean", KeywordCategory::kDataTypes, "Boolean data type", lsp::CompletionItemKind::kClass },
|
||||
{ "int64", KeywordCategory::kDataTypes, "64-bit integer data type", lsp::CompletionItemKind::kClass },
|
||||
{ "real", KeywordCategory::kDataTypes, "Real number data type", lsp::CompletionItemKind::kClass },
|
||||
{ "array", KeywordCategory::kDataTypes, "Array data type", lsp::CompletionItemKind::kClass },
|
||||
|
||||
// Class Types
|
||||
{ "type", KeywordCategory::kClassTypes, "Type declaration", lsp::CompletionItemKind::kKeyword },
|
||||
{ "class", KeywordCategory::kClassTypes, "Class declaration", lsp::CompletionItemKind::kClass },
|
||||
{ "fakeclass", KeywordCategory::kClassTypes, "Fake class declaration", lsp::CompletionItemKind::kClass },
|
||||
{ "new", KeywordCategory::kClassTypes, "Object instantiation", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Class Modifiers
|
||||
{ "override", KeywordCategory::kClassModifiers, "Override method", lsp::CompletionItemKind::kKeyword },
|
||||
{ "overload", KeywordCategory::kClassModifiers, "Overload method", lsp::CompletionItemKind::kKeyword },
|
||||
{ "virtual", KeywordCategory::kClassModifiers, "Virtual method", lsp::CompletionItemKind::kKeyword },
|
||||
{ "property", KeywordCategory::kClassModifiers, "Property declaration", lsp::CompletionItemKind::kProperty },
|
||||
{ "self", KeywordCategory::kClassModifiers, "Self reference", lsp::CompletionItemKind::kVariable },
|
||||
{ "inherited", KeywordCategory::kClassModifiers, "Inherited method call", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Access Modifiers
|
||||
{ "public", KeywordCategory::kAccessModifiers, "Public access modifier", lsp::CompletionItemKind::kKeyword },
|
||||
{ "protected", KeywordCategory::kAccessModifiers, "Protected access modifier", lsp::CompletionItemKind::kKeyword },
|
||||
{ "private", KeywordCategory::kAccessModifiers, "Private access modifier", lsp::CompletionItemKind::kKeyword },
|
||||
{ "published", KeywordCategory::kAccessModifiers, "Published access modifier", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Property Accessors
|
||||
{ "read", KeywordCategory::kPropertyAccessors, "Property read accessor", lsp::CompletionItemKind::kKeyword },
|
||||
{ "write", KeywordCategory::kPropertyAccessors, "Property write accessor", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Constructors
|
||||
{ "create", KeywordCategory::kConstructors, "Constructor method", lsp::CompletionItemKind::kConstructor },
|
||||
{ "destroy", KeywordCategory::kConstructors, "Destructor method", lsp::CompletionItemKind::kMethod },
|
||||
{ "operator", KeywordCategory::kConstructors, "Operator overload", lsp::CompletionItemKind::kOperator },
|
||||
|
||||
// Variable Modifiers
|
||||
{ "external", KeywordCategory::kVariableModifiers, "External declaration", lsp::CompletionItemKind::kKeyword },
|
||||
{ "const", KeywordCategory::kVariableModifiers, "Constant declaration", lsp::CompletionItemKind::kConstant },
|
||||
{ "out", KeywordCategory::kVariableModifiers, "Output parameter", lsp::CompletionItemKind::kKeyword },
|
||||
{ "var", KeywordCategory::kVariableModifiers, "Variable declaration", lsp::CompletionItemKind::kVariable },
|
||||
{ "global", KeywordCategory::kVariableModifiers, "Global variable", lsp::CompletionItemKind::kVariable },
|
||||
{ "static", KeywordCategory::kVariableModifiers, "Static variable", lsp::CompletionItemKind::kVariable },
|
||||
|
||||
// Conditionals
|
||||
{ "if", KeywordCategory::kConditionals, "If statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "else", KeywordCategory::kConditionals, "Else clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "then", KeywordCategory::kConditionals, "Then clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "case", KeywordCategory::kConditionals, "Case statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "of", KeywordCategory::kConditionals, "Of clause", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Loops
|
||||
{ "for", KeywordCategory::kLoops, "For loop", lsp::CompletionItemKind::kKeyword },
|
||||
{ "while", KeywordCategory::kLoops, "While loop", lsp::CompletionItemKind::kKeyword },
|
||||
{ "do", KeywordCategory::kLoops, "Do clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "downto", KeywordCategory::kLoops, "Downto clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "step", KeywordCategory::kLoops, "Step clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "until", KeywordCategory::kLoops, "Until clause", lsp::CompletionItemKind::kKeyword },
|
||||
{ "repeat", KeywordCategory::kLoops, "Repeat loop", lsp::CompletionItemKind::kKeyword },
|
||||
{ "to", KeywordCategory::kLoops, "To clause", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Branch Control
|
||||
{ "break", KeywordCategory::kBranchControl, "Break statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "continue", KeywordCategory::kBranchControl, "Continue statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "goto", KeywordCategory::kBranchControl, "Goto statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "label", KeywordCategory::kBranchControl, "Label declaration", lsp::CompletionItemKind::kKeyword },
|
||||
{ "exit", KeywordCategory::kBranchControl, "Exit statement", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Return Control
|
||||
{ "return", KeywordCategory::kReturnControl, "Return statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "debugreturn", KeywordCategory::kReturnControl, "Debug return statement", lsp::CompletionItemKind::kKeyword },
|
||||
{ "debugrunenv", KeywordCategory::kReturnControl, "Debug run environment", lsp::CompletionItemKind::kKeyword },
|
||||
{ "debugrunenvdo", KeywordCategory::kReturnControl, "Debug run environment do", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Block Control
|
||||
{ "begin", KeywordCategory::kBlockControl, "Begin block", lsp::CompletionItemKind::kKeyword },
|
||||
{ "end", KeywordCategory::kBlockControl, "End block", lsp::CompletionItemKind::kKeyword },
|
||||
{ "with", KeywordCategory::kBlockControl, "With statement", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// References
|
||||
{ "weakref", KeywordCategory::kReferences, "Weak reference", lsp::CompletionItemKind::kKeyword },
|
||||
{ "autoref", KeywordCategory::kReferences, "Auto reference", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Namespace
|
||||
{ "namespace", KeywordCategory::kNamespace, "Namespace declaration", lsp::CompletionItemKind::kModule },
|
||||
|
||||
// Exceptions
|
||||
{ "except", KeywordCategory::kExceptions, "Exception handling", lsp::CompletionItemKind::kKeyword },
|
||||
{ "raise", KeywordCategory::kExceptions, "Raise exception", lsp::CompletionItemKind::kKeyword },
|
||||
{ "try", KeywordCategory::kExceptions, "Try block", lsp::CompletionItemKind::kKeyword },
|
||||
{ "finally", KeywordCategory::kExceptions, "Finally block", lsp::CompletionItemKind::kKeyword },
|
||||
{ "exceptobject", KeywordCategory::kExceptions, "Exception object", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Logical Operators
|
||||
{ "and", KeywordCategory::kLogicalOperators, "Logical AND", lsp::CompletionItemKind::kOperator },
|
||||
{ "in", KeywordCategory::kLogicalOperators, "In operator", lsp::CompletionItemKind::kOperator },
|
||||
{ "is", KeywordCategory::kLogicalOperators, "Is operator", lsp::CompletionItemKind::kOperator },
|
||||
{ "not", KeywordCategory::kLogicalOperators, "Logical NOT", lsp::CompletionItemKind::kOperator },
|
||||
{ "or", KeywordCategory::kLogicalOperators, "Logical OR", lsp::CompletionItemKind::kOperator },
|
||||
|
||||
// Arithmetic Operators
|
||||
{ "div", KeywordCategory::kArithmeticOperators, "Integer division", lsp::CompletionItemKind::kOperator },
|
||||
{ "mod", KeywordCategory::kArithmeticOperators, "Modulo operation", lsp::CompletionItemKind::kOperator },
|
||||
|
||||
// Bitwise Operators
|
||||
{ "ror", KeywordCategory::kBitwiseOperators, "Rotate right", lsp::CompletionItemKind::kOperator },
|
||||
{ "rol", KeywordCategory::kBitwiseOperators, "Rotate left", lsp::CompletionItemKind::kOperator },
|
||||
{ "shr", KeywordCategory::kBitwiseOperators, "Shift right", lsp::CompletionItemKind::kOperator },
|
||||
{ "shl", KeywordCategory::kBitwiseOperators, "Shift left", lsp::CompletionItemKind::kOperator },
|
||||
|
||||
// Set Operators
|
||||
{ "union", KeywordCategory::kSetOperators, "Set union", lsp::CompletionItemKind::kOperator },
|
||||
{ "minus", KeywordCategory::kSetOperators, "Set difference", lsp::CompletionItemKind::kOperator },
|
||||
{ "union2", KeywordCategory::kSetOperators, "Set union (alternative)", lsp::CompletionItemKind::kOperator },
|
||||
|
||||
// SQL Control
|
||||
{ "select", KeywordCategory::kSqlControl, "SQL SELECT", lsp::CompletionItemKind::kKeyword },
|
||||
{ "vselect", KeywordCategory::kSqlControl, "Virtual SELECT", lsp::CompletionItemKind::kKeyword },
|
||||
{ "sselect", KeywordCategory::kSqlControl, "Special SELECT", lsp::CompletionItemKind::kKeyword },
|
||||
{ "update", KeywordCategory::kSqlControl, "SQL UPDATE", lsp::CompletionItemKind::kKeyword },
|
||||
{ "delete", KeywordCategory::kSqlControl, "SQL DELETE", lsp::CompletionItemKind::kKeyword },
|
||||
{ "mselect", KeywordCategory::kSqlControl, "Multiple SELECT", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// SQL Keywords
|
||||
{ "sqlin", KeywordCategory::kSqlKeywords, "SQL IN", lsp::CompletionItemKind::kKeyword },
|
||||
{ "from", KeywordCategory::kSqlKeywords, "SQL FROM", lsp::CompletionItemKind::kKeyword },
|
||||
{ "where", KeywordCategory::kSqlKeywords, "SQL WHERE", lsp::CompletionItemKind::kKeyword },
|
||||
{ "group", KeywordCategory::kSqlKeywords, "SQL GROUP", lsp::CompletionItemKind::kKeyword },
|
||||
{ "by", KeywordCategory::kSqlKeywords, "SQL BY", lsp::CompletionItemKind::kKeyword },
|
||||
{ "order", KeywordCategory::kSqlKeywords, "SQL ORDER", lsp::CompletionItemKind::kKeyword },
|
||||
{ "distinct", KeywordCategory::kSqlKeywords, "SQL DISTINCT", lsp::CompletionItemKind::kKeyword },
|
||||
{ "join", KeywordCategory::kSqlKeywords, "SQL JOIN", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// SQL Operators (note: some overlap with logical operators)
|
||||
{ "on", KeywordCategory::kSqlOperators, "SQL ON", lsp::CompletionItemKind::kOperator },
|
||||
{ "like", KeywordCategory::kSqlOperators, "SQL LIKE", lsp::CompletionItemKind::kOperator },
|
||||
|
||||
// Calling Conventions
|
||||
{ "cdecl", KeywordCategory::kCallingConventions, "C calling convention", lsp::CompletionItemKind::kKeyword },
|
||||
{ "pascal", KeywordCategory::kCallingConventions, "Pascal calling convention", lsp::CompletionItemKind::kKeyword },
|
||||
{ "stdcall", KeywordCategory::kCallingConventions, "Standard calling convention", lsp::CompletionItemKind::kKeyword },
|
||||
{ "safecall", KeywordCategory::kCallingConventions, "Safe calling convention", lsp::CompletionItemKind::kKeyword },
|
||||
{ "fastcall", KeywordCategory::kCallingConventions, "Fast calling convention", lsp::CompletionItemKind::kKeyword },
|
||||
{ "register", KeywordCategory::kCallingConventions, "Register calling convention", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// System Keywords
|
||||
{ "setuid", KeywordCategory::kSystemKeywords, "Set user ID", lsp::CompletionItemKind::kKeyword },
|
||||
{ "sudo", KeywordCategory::kSystemKeywords, "Super user do", lsp::CompletionItemKind::kKeyword },
|
||||
|
||||
// Builtin Variables
|
||||
{ "paramcount", KeywordCategory::kBuiltinVariables, "Parameter count", lsp::CompletionItemKind::kVariable },
|
||||
{ "realparamcount", KeywordCategory::kBuiltinVariables, "Real parameter count", lsp::CompletionItemKind::kVariable },
|
||||
{ "params", KeywordCategory::kBuiltinVariables, "Parameters array", lsp::CompletionItemKind::kVariable },
|
||||
{ "system", KeywordCategory::kBuiltinVariables, "System variable", lsp::CompletionItemKind::kVariable },
|
||||
{ "tslassigning", KeywordCategory::kBuiltinVariables, "TSL assigning", lsp::CompletionItemKind::kVariable },
|
||||
{ "likeeps", KeywordCategory::kBuiltinVariables, "Like EPS", lsp::CompletionItemKind::kVariable },
|
||||
{ "likeepsrate", KeywordCategory::kBuiltinVariables, "Like EPS rate", lsp::CompletionItemKind::kVariable },
|
||||
|
||||
// Builtin Functions
|
||||
{ "echo", KeywordCategory::kBuiltinFunctions, "Echo function", lsp::CompletionItemKind::kFunction },
|
||||
{ "mtic", KeywordCategory::kBuiltinFunctions, "MTIC function", lsp::CompletionItemKind::kFunction },
|
||||
{ "mtoc", KeywordCategory::kBuiltinFunctions, "MTOC function", lsp::CompletionItemKind::kFunction },
|
||||
|
||||
// Boolean Constants
|
||||
{ "false", KeywordCategory::kBooleanConstants, "Boolean false", lsp::CompletionItemKind::kConstant },
|
||||
{ "true", KeywordCategory::kBooleanConstants, "Boolean true", lsp::CompletionItemKind::kConstant },
|
||||
|
||||
// Math Constants
|
||||
{ "inf", KeywordCategory::kMathConstants, "Infinity", lsp::CompletionItemKind::kConstant },
|
||||
{ "nan", KeywordCategory::kMathConstants, "Not a Number", lsp::CompletionItemKind::kConstant },
|
||||
|
||||
// Null Constants
|
||||
{ "nil", KeywordCategory::kNullConstants, "Null value", lsp::CompletionItemKind::kConstant }
|
||||
};
|
||||
keyword_set_.clear();
|
||||
for (const auto& keyword : keywords_)
|
||||
keyword_set_.insert(ToLowerCase(keyword.keyword));
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
std::string TslKeywords::ToLowerCase(const std::string& str)
|
||||
{
|
||||
std::string result = str;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TslKeywords::StartsWith(const std::string& str, const std::string& prefix)
|
||||
{
|
||||
if (prefix.length() > str.length())
|
||||
return false;
|
||||
return str.compare(0, prefix.length(), prefix) == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include "../lsp/lsp_types.hpp"
|
||||
|
||||
namespace tsl
|
||||
{
|
||||
enum class KeywordCategory
|
||||
{
|
||||
kProgramStructure, // program, function, procedure, unit, uses, implementation, interface, initialization, finalization
|
||||
kDataTypes, // string, integer, boolean, int64, real, array
|
||||
kClassTypes, // type, class, fakeclass, new
|
||||
kClassModifiers, // override, overload, virtual, property, self, inherited
|
||||
kAccessModifiers, // public, protected, private, published
|
||||
kPropertyAccessors, // read, write
|
||||
kConstructors, // create, destroy, operator
|
||||
kVariableModifiers, // external, const, out, var, global, static
|
||||
kConditionals, // if, else, then, case, of
|
||||
kLoops, // for, while, do, downto, step, until, repeat, to
|
||||
kBranchControl, // break, continue, goto, label, exit
|
||||
kReturnControl, // return, debugreturn, debugrunenv, debugrunenvdo
|
||||
kBlockControl, // begin, end, with
|
||||
kReferences, // weakref, autoref
|
||||
kNamespace, // namespace
|
||||
kExceptions, // except, raise, try, finally, exceptobject
|
||||
kLogicalOperators, // and, in, is, not, or
|
||||
kArithmeticOperators, // div, mod
|
||||
kBitwiseOperators, // ror, rol, shr, shl
|
||||
kSetOperators, // union, minus, union2
|
||||
kSqlControl, // select, vselect, sselect, update, delete, mselect
|
||||
kSqlOperators, // on, like, in (SQL context)
|
||||
kSqlKeywords, // sqlin, from, where, group, by, order, distinct, join
|
||||
kCallingConventions, // cdecl, pascal, stdcall, safecall, fastcall, register
|
||||
kSystemKeywords, // setuid, sudo
|
||||
kBuiltinVariables, // paramcount, realparamcount, params, system, tslassigning, likeeps, likeepsrate
|
||||
kBuiltinFunctions, // echo, mtic, mtoc
|
||||
kBooleanConstants, // false, true
|
||||
kMathConstants, // inf, nan
|
||||
kNullConstants // nil
|
||||
};
|
||||
|
||||
// 关键字信息
|
||||
struct KeywordInfo
|
||||
{
|
||||
std::string keyword;
|
||||
KeywordCategory category;
|
||||
std::string description;
|
||||
lsp::CompletionItemKind completion_kind;
|
||||
};
|
||||
|
||||
class TslKeywords
|
||||
{
|
||||
public:
|
||||
static const std::vector<KeywordInfo>& GetAllKeywords();
|
||||
static std::vector<KeywordInfo> GetKeywordsByCategory(KeywordCategory category);
|
||||
static std::vector<lsp::CompletionItem> GetCompletionItems(const std::string& prefix = "");
|
||||
static bool IsKeyword(const std::string& word);
|
||||
static KeywordCategory GetKeywordCategory(const std::string& word);
|
||||
|
||||
private:
|
||||
static void InitKeywords();
|
||||
static std::string ToLowerCase(const std::string& str);
|
||||
static bool StartsWith(const std::string& str, const std::string& prefix);
|
||||
|
||||
private:
|
||||
static std::vector<KeywordInfo> keywords_;
|
||||
static std::unordered_set<std::string> keyword_set_;
|
||||
static bool initialized_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "./dispacther.hpp"
|
||||
|
||||
namespace lsp {
|
||||
|
||||
|
||||
void RequestDispatcher::RegisterProvider(const std::string& method, RequestProvider handler)
|
||||
{
|
||||
providers_[method] = std::move(handler);
|
||||
}
|
||||
|
||||
nlohmann::json RequestDispatcher::Dispatch(const LspRequest& request)
|
||||
{
|
||||
auto it = providers_.find(request.method);
|
||||
if (it != providers_.end()) {
|
||||
try
|
||||
{
|
||||
return it->second(request);
|
||||
} catch (const std::exception& e)
|
||||
{
|
||||
return HandleException(request, e.what());
|
||||
}
|
||||
}
|
||||
return HandleUnknownMethod(request);
|
||||
}
|
||||
|
||||
bool RequestDispatcher::SupportsMethod(const std::string& method) const
|
||||
{
|
||||
return providers_.find(method) != providers_.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> RequestDispatcher::GetSupportedMethods() const
|
||||
{
|
||||
std::vector<std::string> methods;
|
||||
for (const auto& pair : providers_) {
|
||||
methods.push_back(pair.first);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
nlohmann::json RequestDispatcher::HandleUnknownMethod(const LspRequest& request)
|
||||
{
|
||||
nlohmann::json resp;
|
||||
resp["jsonrpc"] = "2.0";
|
||||
resp["id"] = request.id;
|
||||
resp["error"] = {
|
||||
{"code", -32601},
|
||||
{"message", "Method not found: " + request.method}
|
||||
};
|
||||
return resp;
|
||||
}
|
||||
|
||||
nlohmann::json RequestDispatcher::HandleException(const LspRequest& request, const std::string& error_message)
|
||||
{
|
||||
nlohmann::json resp;
|
||||
resp["jsonrpc"] = "2.0";
|
||||
resp["id"] = request.id;
|
||||
resp["error"] = {
|
||||
{"code", -32603}, // Internal error
|
||||
{"message", "Internal error: " + error_message}
|
||||
};
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "./lsp_types.hpp"
|
||||
|
||||
namespace lsp
|
||||
{
|
||||
|
||||
// 请求处理函数类型
|
||||
using RequestProvider = std::function<nlohmann::json(const LspRequest&)>;
|
||||
|
||||
class RequestDispatcher
|
||||
{
|
||||
public:
|
||||
RequestDispatcher() = default;
|
||||
void RegisterProvider(const std::string& method, RequestProvider provider); // 可选:重命名
|
||||
nlohmann::json Dispatch(const LspRequest& request);
|
||||
bool SupportsMethod(const std::string& method) const;
|
||||
std::vector<std::string> GetSupportedMethods() const;
|
||||
|
||||
private:
|
||||
nlohmann::json HandleUnknownMethod(const LspRequest& request);
|
||||
nlohmann::json HandleException(const LspRequest& request, const std::string& error_message);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, RequestProvider> providers_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "./logger.hpp"
|
||||
|
||||
namespace lsp::log
|
||||
{
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
if (file_stream_.is_open())
|
||||
file_stream_.close();
|
||||
}
|
||||
|
||||
Logger& Logger::Instance()
|
||||
{
|
||||
static Logger logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void Logger::SetLevel(LogLevel level)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
level_ = level;
|
||||
}
|
||||
|
||||
void Logger::SetLogFile(const std::string& filename)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (file_stream_.is_open())
|
||||
file_stream_.close();
|
||||
file_stream_.open(filename, std::ios::app);
|
||||
use_file_ = file_stream_.is_open();
|
||||
}
|
||||
|
||||
void Logger::EnableStderr(bool enable)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
use_stderr_ = enable;
|
||||
}
|
||||
|
||||
const char* Logger::LevelToString(LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel::kOff:
|
||||
return "OFF";
|
||||
case LogLevel::kError:
|
||||
return "ERROR";
|
||||
case LogLevel::kWarn:
|
||||
return "WARN";
|
||||
case LogLevel::kInfo:
|
||||
return "INFO";
|
||||
case LogLevel::kDebug:
|
||||
return "DEBUG";
|
||||
case LogLevel::kVerbose:
|
||||
return "VERBOSE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
|
||||
namespace lsp::log
|
||||
{
|
||||
enum class LogLevel
|
||||
{
|
||||
kOff = 0,
|
||||
kError = 1,
|
||||
kWarn = 2,
|
||||
kInfo = 3,
|
||||
kDebug = 4,
|
||||
kVerbose = 5
|
||||
};
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
static Logger& Instance();
|
||||
void SetLevel(LogLevel level);
|
||||
void SetLogFile(const std::string& filename);
|
||||
void EnableStderr(bool enable);
|
||||
|
||||
template<typename... Args>
|
||||
void log(LogLevel level, Args&&... args) {
|
||||
if (level > level_)
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "[TSL-LSP:" << LevelToString(level) << "] ";
|
||||
(oss << ... << args);
|
||||
oss << std::endl;
|
||||
|
||||
std::string message = oss.str();
|
||||
|
||||
if (use_stderr_)
|
||||
{
|
||||
std::cerr << message;
|
||||
}
|
||||
|
||||
if (use_file_ && file_stream_.is_open())
|
||||
{
|
||||
file_stream_ << message;
|
||||
file_stream_.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Logger() = default;
|
||||
~Logger();
|
||||
Logger(const Logger&) = delete;
|
||||
Logger& operator=(const Logger&) = delete;
|
||||
|
||||
const char* LevelToString(LogLevel level);
|
||||
|
||||
private:
|
||||
LogLevel level_;
|
||||
bool use_file_ = false;
|
||||
bool use_stderr_ = false;
|
||||
std::ofstream file_stream_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
void Error(Args&&... args) {
|
||||
Logger::Instance().log(LogLevel::kError, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Warn(Args&&... args) {
|
||||
Logger::Instance().log(LogLevel::kWarn, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Info(Args&&... args) {
|
||||
Logger::Instance().log(LogLevel::kInfo, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Debug(Args&&... args) {
|
||||
Logger::Instance().log(LogLevel::kDebug, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Verbose(Args&&... args) {
|
||||
Logger::Instance().log(LogLevel::kVerbose, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace lsp
|
||||
{
|
||||
// LSP请求结构
|
||||
struct LspRequest
|
||||
{
|
||||
std::string jsonrpc;
|
||||
std::string method;
|
||||
nlohmann::json params;
|
||||
nlohmann::json id;
|
||||
|
||||
LspRequest(const nlohmann::json& json) :
|
||||
jsonrpc(json.value("jsonrpc", "2.0")),
|
||||
method(json.value("method", "")),
|
||||
params(json.value("params", nlohmann::json::object())),
|
||||
id(json.value("id", nlohmann::json::array())) {}
|
||||
};
|
||||
|
||||
// 补全项类型枚举
|
||||
enum class CompletionItemKind
|
||||
{
|
||||
kText = 1,
|
||||
kMethod = 2,
|
||||
kFunction = 3,
|
||||
kConstructor = 4,
|
||||
kField = 5,
|
||||
kVariable = 6,
|
||||
kClass = 7,
|
||||
kInterface = 8,
|
||||
kModule = 9,
|
||||
kProperty = 10,
|
||||
kUnit = 11,
|
||||
kValue = 12,
|
||||
kEnum = 13,
|
||||
kKeyword = 14,
|
||||
kSnippet = 15,
|
||||
kColor = 16,
|
||||
kFile = 17,
|
||||
kReference = 18,
|
||||
kFolder = 19,
|
||||
kEnumMember = 20,
|
||||
kConstant = 21,
|
||||
kStruct = 22,
|
||||
kEvent = 23,
|
||||
kOperator = 24,
|
||||
kTypeParameter = 25
|
||||
};
|
||||
|
||||
// 补全项目
|
||||
struct CompletionItem
|
||||
{
|
||||
std::string label;
|
||||
CompletionItemKind kind;
|
||||
std::optional<std::string> detail;
|
||||
std::optional<std::string> documentation;
|
||||
std::optional<std::string> insert_text;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include "../provider/base/provider_registry.hpp"
|
||||
#include "./server.hpp"
|
||||
#include "./logger.hpp"
|
||||
|
||||
namespace lsp
|
||||
{
|
||||
LspServer::LspServer()
|
||||
{
|
||||
providers::RegisterAllProviders(dispatcher_);
|
||||
log::Debug("LSP server initialized with providers.");
|
||||
}
|
||||
|
||||
void LspServer::Run()
|
||||
{
|
||||
log::Info("LSP server starting main loop...");
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::optional<std::string> message = ReadMessage();
|
||||
if (!message) continue;
|
||||
nlohmann::json response = HandleRequest(*message);
|
||||
if (!response.empty())
|
||||
SendResponse(response);
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
log::Error("Error processing message: ", e.what());
|
||||
}
|
||||
}
|
||||
log::Info("LSP server main loop ended");
|
||||
}
|
||||
|
||||
std::optional<std::string> LspServer::ReadMessage()
|
||||
{
|
||||
std::string line;
|
||||
size_t content_length = 0;
|
||||
|
||||
// 读取 LSP Header
|
||||
while (std::getline(std::cin, line))
|
||||
{
|
||||
log::Verbose("Received header line: ", line);
|
||||
|
||||
// 去掉尾部 \r
|
||||
if (!line.empty() && line.back() == '\r')
|
||||
line.pop_back();
|
||||
if (line.empty())
|
||||
break;
|
||||
// 查找 Content-Length
|
||||
if (line.find("Content-Length:") == 0)
|
||||
{
|
||||
std::string length_str = line.substr(15); // 跳过 "Content-Length:"
|
||||
size_t start = length_str.find_first_not_of(" ");
|
||||
if (start != std::string::npos)
|
||||
{
|
||||
length_str = length_str.substr(start);
|
||||
try
|
||||
{
|
||||
content_length = std::stoul(length_str);
|
||||
log::Verbose("Content-Length: ", content_length);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::Error("Failed to parse Content-Length: ", e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content_length == 0)
|
||||
{
|
||||
log::Warn("No Content-Length found in header");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 读取内容体
|
||||
std::string body(content_length, '\0');
|
||||
std::cin.read(&body[0], content_length);
|
||||
|
||||
log::Verbose("Message body: ", body);
|
||||
|
||||
if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
|
||||
{
|
||||
log::Error("Read incomplete message body, expected: ", content_length, ", got: ", std::cin.gcount());
|
||||
return std::nullopt;
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
nlohmann::json LspServer::HandleRequest(const std::string& raw_request)
|
||||
{
|
||||
try
|
||||
{
|
||||
nlohmann::json json = nlohmann::json::parse(raw_request);
|
||||
LspRequest request(json);
|
||||
log::Debug("Processing method: ", request.method);
|
||||
|
||||
nlohmann::json response = dispatcher_.Dispatch(request);
|
||||
return response;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::Error("Failed to handle request: ", e.what());
|
||||
return nlohmann::json();
|
||||
}
|
||||
}
|
||||
|
||||
void LspServer::SendResponse(const nlohmann::json& response)
|
||||
{
|
||||
std::string response_str = response.dump();
|
||||
size_t byte_length = response_str.length();
|
||||
|
||||
// 调试:显示实际发送的原始内容
|
||||
log::Debug("Response length: ", byte_length);
|
||||
log::Debug("Raw response content: [", response_str, "]");
|
||||
|
||||
std::cout << "Content-Length: " << byte_length << "\r\n\r\n";
|
||||
std::cout << response_str;
|
||||
std::cout.flush();
|
||||
|
||||
log::Verbose("Response sent successfully");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "./dispacther.hpp"
|
||||
|
||||
namespace lsp
|
||||
{
|
||||
|
||||
class LspServer
|
||||
{
|
||||
public:
|
||||
LspServer();
|
||||
~LspServer() = default;
|
||||
void Run();
|
||||
|
||||
private:
|
||||
// 读取LSP消息
|
||||
std::optional<std::string> ReadMessage();
|
||||
// 处理LSP请求
|
||||
nlohmann::json HandleRequest(const std::string& raw_request);
|
||||
// 发送LSP响应
|
||||
void SendResponse(const nlohmann::json& response);
|
||||
|
||||
private:
|
||||
RequestDispatcher dispatcher_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include "./lsp/server.hpp"
|
||||
#include "./lsp/logger.hpp"
|
||||
|
||||
void ParseArgs(int argc, char* argv[])
|
||||
{
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg == "--log=verbose")
|
||||
{
|
||||
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kVerbose);
|
||||
} else if (arg == "--log=debug")
|
||||
{
|
||||
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kDebug);
|
||||
} else if (arg == "--log=info")
|
||||
{
|
||||
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kInfo);
|
||||
} else if (arg == "--log=warn")
|
||||
{
|
||||
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kWarn);
|
||||
} else if (arg == "--log=error")
|
||||
{
|
||||
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kError);
|
||||
} else if (arg == "--log=off")
|
||||
{
|
||||
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kOff);
|
||||
} else if (arg.find("--log-file=") == 0)
|
||||
{
|
||||
std::string filename = arg.substr(11); // 跳过 "--log-file="
|
||||
lsp::log::Logger::Instance().SetLogFile(filename);
|
||||
}
|
||||
else if (arg == "--log-stderr")
|
||||
{
|
||||
lsp::log::Logger::Instance().EnableStderr(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
ParseArgs(argc, argv);
|
||||
try
|
||||
{
|
||||
lsp::log::Info("TSL-LSP server starting...");
|
||||
lsp::LspServer server;
|
||||
server.Run();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "[TSL-LSP] Server fatal error: " << e.what() << std::endl;
|
||||
lsp::log::Error("Server fatal error: ", e.what());
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "[TSL-LSP] Server unknown fatal error" << std::endl;
|
||||
lsp::log::Error("Server unknown fatal error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
lsp::log::Info("TSL-LSP server stopped normally");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../../lsp/lsp_types.hpp"
|
||||
|
||||
namespace lsp::providers
|
||||
{
|
||||
|
||||
// LSP请求提供者接口基类
|
||||
class ILspProvider
|
||||
{
|
||||
public:
|
||||
virtual ~ILspProvider() = default;
|
||||
|
||||
// 处理LSP请求
|
||||
virtual nlohmann::json ProvideResponse(const LspRequest& request) = 0;
|
||||
// 获取支持的LSP方法名
|
||||
virtual std::string GetMethod() const = 0;
|
||||
// 获取提供者名称(用于日志和调试)
|
||||
virtual std::string GetProviderName() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "./provider_registry.hpp"
|
||||
#include "../initialize/initialize_provider.hpp"
|
||||
#include "../initialized/initialized_provider.hpp"
|
||||
#include "../text_document/did_open_provider.hpp"
|
||||
#include "../text_document/did_change_provider.hpp"
|
||||
#include "../text_document/completion_provider.hpp"
|
||||
#include "../trace/set_trace_provider.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers
|
||||
{
|
||||
|
||||
void RegisterAllProviders(RequestDispatcher& dispatcher)
|
||||
{
|
||||
log::Info("Registering LSP providers...");
|
||||
|
||||
RegisterProvider<initialize::InitializeProvider>(dispatcher);
|
||||
RegisterProvider<initialized::InitializedProvider>(dispatcher);
|
||||
RegisterProvider<text_document::DidOpenProvider>(dispatcher);
|
||||
RegisterProvider<text_document::DidChangeProvider>(dispatcher);
|
||||
RegisterProvider<text_document::CompletionProvider>(dispatcher);
|
||||
RegisterProvider<trace::SetTraceProvider>(dispatcher);
|
||||
|
||||
log::Info("Successfully registered ", dispatcher.GetSupportedMethods().size(), " LSP providers");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "../../lsp/dispacther.hpp"
|
||||
#include "./provider_interface.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers
|
||||
{
|
||||
|
||||
// 模板函数:注册provider
|
||||
template<typename ProviderClass>
|
||||
void RegisterProvider(RequestDispatcher& dispatcher)
|
||||
{
|
||||
static_assert(std::is_base_of_v<ILspProvider, ProviderClass>,
|
||||
"Provider must inherit from ILspProvider");
|
||||
|
||||
auto provider = std::make_shared<ProviderClass>();
|
||||
|
||||
log::Info("Registering ", provider->GetProviderName(), " for method: ", provider->GetMethod());
|
||||
|
||||
dispatcher.RegisterProvider(
|
||||
provider->GetMethod(),
|
||||
[provider](const LspRequest& request) -> nlohmann::json {
|
||||
return provider->ProvideResponse(request);
|
||||
});
|
||||
}
|
||||
|
||||
// 批量注册provider
|
||||
void RegisterAllProviders(RequestDispatcher& dispatcher);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "./initialize_provider.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers::initialize
|
||||
{
|
||||
|
||||
nlohmann::json InitializeProvider::ProvideResponse(const LspRequest& request)
|
||||
{
|
||||
nlohmann::json response;
|
||||
response["jsonrpc"] = "2.0";
|
||||
response["id"] = request.id;
|
||||
response["result"] = BuildInitializeResult();
|
||||
log::Debug("InitializeProvider: Providing response for method '", request.method, "' with id ", request.id);
|
||||
return response;
|
||||
}
|
||||
|
||||
inline std::string InitializeProvider::GetMethod() const
|
||||
{
|
||||
return "initialize";
|
||||
}
|
||||
|
||||
inline std::string InitializeProvider::GetProviderName() const
|
||||
{
|
||||
return "InitializeProvider";
|
||||
}
|
||||
|
||||
nlohmann::json InitializeProvider::BuildInitializeResult()
|
||||
{
|
||||
nlohmann::json result;
|
||||
result["capabilities"] = BuildServerCapabilities();
|
||||
result["serverInfo"] = BuildServerInfo();
|
||||
return result;
|
||||
}
|
||||
|
||||
nlohmann::json InitializeProvider::BuildServerCapabilities()
|
||||
{
|
||||
nlohmann::json capabilities;
|
||||
capabilities["textDocumentSync"] = nlohmann::json();
|
||||
capabilities["textDocumentSync"]["change"] = 2;
|
||||
capabilities["textDocumentSync"]["openClose"] = true;
|
||||
capabilities["textDocumentSync"]["save"] = true;
|
||||
capabilities["completionProvider"] = nlohmann::json();
|
||||
capabilities["completionProvider"]["resolveProvider"] = false;
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
nlohmann::json InitializeProvider::BuildServerInfo()
|
||||
{
|
||||
nlohmann::json serverInfo;
|
||||
serverInfo["name"] = "TSL Language Server";
|
||||
serverInfo["version"] = "1.0.0";
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "../base/provider_interface.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace lsp::providers::initialize
|
||||
{
|
||||
class InitializeProvider : public ILspProvider
|
||||
{
|
||||
public:
|
||||
InitializeProvider() = default;
|
||||
nlohmann::json ProvideResponse(const LspRequest& request) override;
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
|
||||
private:
|
||||
nlohmann::json BuildServerCapabilities();
|
||||
nlohmann::json BuildServerInfo();
|
||||
nlohmann::json BuildInitializeResult();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "./initialized_provider.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers::initialized
|
||||
{
|
||||
|
||||
nlohmann::json InitializedProvider::ProvideResponse(const LspRequest& request)
|
||||
{
|
||||
log::Debug("InitializedProvider: Providing response for method '", request.method, "' with id ", request.id);
|
||||
return nlohmann::json();
|
||||
}
|
||||
|
||||
inline std::string InitializedProvider::GetMethod() const
|
||||
{
|
||||
return "initialized";
|
||||
}
|
||||
|
||||
inline std::string InitializedProvider::GetProviderName() const
|
||||
{
|
||||
return "InitializedProvider";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "../base/provider_interface.hpp"
|
||||
|
||||
namespace lsp::providers::initialized
|
||||
{
|
||||
class InitializedProvider : public ILspProvider
|
||||
{
|
||||
public:
|
||||
InitializedProvider() = default;
|
||||
nlohmann::json ProvideResponse(const LspRequest& request) override;
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "./completion_provider.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers::text_document
|
||||
{
|
||||
nlohmann::json CompletionProvider::ProvideResponse(const LspRequest& request)
|
||||
{
|
||||
log::Debug("CompletionProvider: Providing response for method '", request.method, "' with id ", request.id);
|
||||
try
|
||||
{
|
||||
nlohmann::json response = BuildCompletionResponse(request);
|
||||
return response;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::Error(GetProviderName(), ": Error - ", e.what());
|
||||
nlohmann::json errorResponse = CreateErrorResponse(request.id, -32603, e.what());
|
||||
return errorResponse;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string CompletionProvider::GetMethod() const
|
||||
{
|
||||
return "textDocument/completion";
|
||||
}
|
||||
|
||||
inline std::string CompletionProvider::GetProviderName() const
|
||||
{
|
||||
return "CompletionProvider";
|
||||
}
|
||||
|
||||
nlohmann::json CompletionProvider::BuildCompletionResponse(const LspRequest& request)
|
||||
{
|
||||
nlohmann::json response;
|
||||
response["jsonrpc"] = "2.0";
|
||||
response["id"] = request.id;
|
||||
|
||||
// 验证必要参数
|
||||
if (!request.params.contains("textDocument") || !request.params.contains("position"))
|
||||
{
|
||||
log::Warn(GetProviderName(), ": Missing required parameters in request");
|
||||
// 返回空补全列表而非错误
|
||||
response["result"] = BuildCompletionResult({});
|
||||
return response;
|
||||
}
|
||||
|
||||
// 提取参数
|
||||
std::string uri = ExtractDocumentUri(request.params);
|
||||
nlohmann::json position = ExtractPosition(request.params);
|
||||
std::string prefix = ExtractPrefix(request.params);
|
||||
|
||||
log::Verbose(
|
||||
GetProviderName(), ": Processing completion request for URI='", uri, "', Position=", position.dump(), ", Prefix='", prefix, "'");
|
||||
|
||||
// 收集所有补全项
|
||||
std::vector<CompletionItem> allItems;
|
||||
|
||||
// 添加关键字补全
|
||||
auto keywordItems = ProvideKeywordCompletions(prefix);
|
||||
allItems.insert(allItems.end(), keywordItems.begin(), keywordItems.end());
|
||||
|
||||
// 添加上下文相关补全
|
||||
auto contextualItems = ProvideContextualCompletions(uri, position, prefix);
|
||||
allItems.insert(allItems.end(), contextualItems.begin(), contextualItems.end());
|
||||
|
||||
// 构建响应
|
||||
response["result"] = BuildCompletionResult(allItems);
|
||||
|
||||
log::Info(GetProviderName(), ": Provided ", allItems.size(), " completion items");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
nlohmann::json CompletionProvider::BuildCompletionResult(const std::vector<CompletionItem>& items)
|
||||
{
|
||||
nlohmann::json result;
|
||||
result["isIncomplete"] = false; // 表示这是完整的补全列表
|
||||
result["items"] = nlohmann::json::array();
|
||||
|
||||
for (const auto& item : items)
|
||||
result["items"].push_back(CompletionItemToJson(item));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string CompletionProvider::ExtractDocumentUri(const nlohmann::json& params)
|
||||
{
|
||||
if (params.contains("textDocument") && params["textDocument"].contains("uri"))
|
||||
{
|
||||
std::string uri = params["textDocument"]["uri"].get<std::string>();
|
||||
log::Verbose("ExtractDocumentUri: Found URI: ", uri);
|
||||
return uri;
|
||||
}
|
||||
log::Warn("ExtractDocumentUri: No URI found in parameters");
|
||||
return "";
|
||||
}
|
||||
|
||||
nlohmann::json CompletionProvider::ExtractPosition(const nlohmann::json& params)
|
||||
{
|
||||
if (params.contains("position"))
|
||||
{
|
||||
nlohmann::json pos = params["position"];
|
||||
log::Verbose("ExtractPosition: Found position: ", pos.dump());
|
||||
return pos;
|
||||
}
|
||||
// 返回默认位置
|
||||
nlohmann::json defaultPos;
|
||||
defaultPos["line"] = 0;
|
||||
defaultPos["character"] = 0;
|
||||
log::Warn("ExtractPosition: No position found in parameters, using default (0, 0)");
|
||||
return defaultPos;
|
||||
}
|
||||
|
||||
std::string CompletionProvider::ExtractPrefix(const nlohmann::json& params)
|
||||
{
|
||||
// 方法1: 直接从参数中获取prefix
|
||||
if (params.contains("prefix"))
|
||||
{
|
||||
std::string prefix = params["prefix"].get<std::string>();
|
||||
log::Verbose("ExtractPrefix: Found prefix form params: '", prefix, "'");
|
||||
return prefix;
|
||||
}
|
||||
|
||||
// 方法2: 从context中获取prefix
|
||||
if (params.contains("context") && params["context"].contains("prefix"))
|
||||
{
|
||||
std::string prefix = params["context"]["prefix"].get<std::string>();
|
||||
log::Verbose("ExtractPrefix: Found prefix form params: '", prefix, "'");
|
||||
return prefix;
|
||||
}
|
||||
|
||||
// TODO: 理想情况下,应该从文档内容和位置计算前缀
|
||||
// 这需要维护文档内容的状态
|
||||
log::Verbose("ExtractPrefix: No prefix found, returning empty string");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<CompletionItem> CompletionProvider::ProvideKeywordCompletions(const std::string& prefix)
|
||||
{
|
||||
std::vector<CompletionItem> items;
|
||||
|
||||
// 从tsl_keywords_获取补全项
|
||||
auto tslItems = tsl_keywords_.GetCompletionItems(prefix);
|
||||
|
||||
for (const auto& tslItem : tslItems)
|
||||
{
|
||||
CompletionItem item;
|
||||
item.label = tslItem.label;
|
||||
item.kind = CompletionItemKind::kKeyword; // LSP CompletionItemKind.Keyword
|
||||
item.detail = "TSL Keyword";
|
||||
item.documentation = "TSL language keyword";
|
||||
item.insert_text = tslItem.label;
|
||||
|
||||
items.push_back(item);
|
||||
}
|
||||
log::Debug("ProvideKeywordCompletions: Found ", items.size(), " keyword completions");
|
||||
return items;
|
||||
}
|
||||
|
||||
std::vector<CompletionItem> CompletionProvider::ProvideContextualCompletions(
|
||||
const std::string& uri,
|
||||
const nlohmann::json& position,
|
||||
const std::string& prefix)
|
||||
{
|
||||
log::Debug("ProvideContextualCompletions: Processing contextual completions for URI: ", uri);
|
||||
std::vector<CompletionItem> items;
|
||||
|
||||
// TODO: 基于上下文提供补全
|
||||
// 这里可以添加:
|
||||
// - 变量名补全
|
||||
// - 函数名补全
|
||||
// - 类型补全
|
||||
// - 属性补全等
|
||||
log::Debug("ProvideContextualCompletions: Found ", items.size(), " contextual completions");
|
||||
return items;
|
||||
}
|
||||
|
||||
nlohmann::json CompletionProvider::CompletionItemToJson(const CompletionItem& item)
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["label"] = item.label;
|
||||
json["kind"] = item.kind;
|
||||
|
||||
if (!item.detail)
|
||||
json["detail"] = item.detail;
|
||||
|
||||
if (!item.documentation)
|
||||
json["documentation"] = item.documentation;
|
||||
|
||||
if (!item.insert_text)
|
||||
json["insertText"] = item.insert_text;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
nlohmann::json CompletionProvider::CreateErrorResponse(const nlohmann::json& id, int code, const std::string& message)
|
||||
{
|
||||
nlohmann::json response;
|
||||
response["jsonrpc"] = "2.0";
|
||||
response["id"] = id;
|
||||
|
||||
nlohmann::json error;
|
||||
error["code"] = code;
|
||||
error["message"] = GetProviderName() + ": " + message;
|
||||
|
||||
response["error"] = error;
|
||||
log::Error("CreateErrorResponse: Created error response with code ", code, " and message: ", message);
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../base/provider_interface.hpp"
|
||||
#include "../../lsp/lsp_types.hpp"
|
||||
#include "../../language/tsl_keywords.hpp"
|
||||
|
||||
namespace lsp::providers::text_document
|
||||
{
|
||||
class CompletionProvider : public ILspProvider
|
||||
{
|
||||
public:
|
||||
CompletionProvider() = default;
|
||||
|
||||
nlohmann::json ProvideResponse(const LspRequest& request) override;
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
|
||||
private:
|
||||
// 构建完整的补全响应
|
||||
nlohmann::json BuildCompletionResponse(const LspRequest& request);
|
||||
|
||||
// 构建补全结果
|
||||
nlohmann::json BuildCompletionResult(const std::vector<CompletionItem>& items);
|
||||
|
||||
// 从请求中提取文档信息
|
||||
std::string ExtractDocumentUri(const nlohmann::json& params);
|
||||
nlohmann::json ExtractPosition(const nlohmann::json& params);
|
||||
|
||||
// 获取补全前缀
|
||||
std::string ExtractPrefix(const nlohmann::json& params);
|
||||
|
||||
// 提供不同类型的补全
|
||||
std::vector<CompletionItem> ProvideKeywordCompletions(const std::string& prefix);
|
||||
std::vector<CompletionItem> ProvideContextualCompletions(
|
||||
const std::string& uri,
|
||||
const nlohmann::json& position,
|
||||
const std::string& prefix);
|
||||
|
||||
// 将CompletionItem转换为JSON
|
||||
nlohmann::json CompletionItemToJson(const CompletionItem& item);
|
||||
|
||||
// 创建错误响应
|
||||
nlohmann::json CreateErrorResponse(const nlohmann::json& id, int code, const std::string& message);
|
||||
|
||||
private:
|
||||
tsl::TslKeywords tsl_keywords_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "./did_change_provider.hpp"
|
||||
#include "./did_open_provider.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers::text_document
|
||||
{
|
||||
|
||||
nlohmann::json DidChangeProvider::ProvideResponse(const LspRequest& request)
|
||||
{
|
||||
log::Debug("DidChangeProvider: Providing response for method '", request.method, "' with id ", request.id);
|
||||
try
|
||||
{
|
||||
auto params = request.params;
|
||||
if (params.contains("textDocument") && params.contains("contentChanges"))
|
||||
{
|
||||
auto textDoc = params["textDocument"];
|
||||
std::string uri = textDoc["uri"];
|
||||
auto changes = params["contentChanges"];
|
||||
|
||||
ApplyContentChanges(uri, changes);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// 处理错误,但不返回错误响应,因为这是通知
|
||||
}
|
||||
|
||||
// 通知不需要响应
|
||||
return nlohmann::json();
|
||||
}
|
||||
|
||||
inline std::string DidChangeProvider::GetMethod() const
|
||||
{
|
||||
return "textDocument/didChange";
|
||||
}
|
||||
|
||||
inline std::string DidChangeProvider::GetProviderName() const
|
||||
{
|
||||
return "DidChangeProvider";
|
||||
}
|
||||
|
||||
void DidChangeProvider::ApplyContentChanges(const std::string& uri, const nlohmann::json& changes)
|
||||
{
|
||||
// 简化实现:假设是全文替换
|
||||
for (const auto& change : changes)
|
||||
{
|
||||
if (change.contains("text"))
|
||||
{
|
||||
// 如果没有range,表示全文替换
|
||||
if (!change.contains("range"))
|
||||
{
|
||||
DidOpenProvider::document_store[uri] = change["text"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// 这里可以实现增量更新,现在简化为全文替换
|
||||
DidOpenProvider::document_store[uri] = change["text"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "../base/provider_interface.hpp"
|
||||
|
||||
namespace lsp::providers::text_document
|
||||
{
|
||||
class DidChangeProvider : public ILspProvider
|
||||
{
|
||||
public:
|
||||
DidChangeProvider() = default;
|
||||
nlohmann::json ProvideResponse(const LspRequest& request) override;
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
|
||||
private:
|
||||
void ApplyContentChanges(const std::string& uri, const nlohmann::json& changes);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "./did_open_provider.hpp"
|
||||
#include "../../lsp/logger.hpp"
|
||||
|
||||
namespace lsp::providers::text_document
|
||||
{
|
||||
std::unordered_map<std::string, std::string> DidOpenProvider::document_store;
|
||||
|
||||
nlohmann::json DidOpenProvider::ProvideResponse(const LspRequest& request)
|
||||
{
|
||||
log::Debug("DidOpenProvider: Providing response for method '", request.method, "' with id ", request.id);
|
||||
try
|
||||
{
|
||||
auto params = request.params;
|
||||
if (params.contains("textDocument"))
|
||||
{
|
||||
auto textDoc = params["textDocument"];
|
||||
std::string uri = textDoc["uri"];
|
||||
std::string text = textDoc["text"];
|
||||
|
||||
// 存储文档内容
|
||||
document_store[uri] = text;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// 处理错误,但不返回错误响应,因为这是通知
|
||||
}
|
||||
// 通知不需要响应
|
||||
return nlohmann::json();
|
||||
}
|
||||
|
||||
inline std::string DidOpenProvider::GetMethod() const
|
||||
{
|
||||
return "textDocument/didOpen";
|
||||
}
|
||||
|
||||
inline std::string DidOpenProvider::GetProviderName() const
|
||||
{
|
||||
return "DidOpenProvider";
|
||||
}
|
||||
|
||||
std::string DidOpenProvider::GetDocumentContent(const std::string& uri)
|
||||
{
|
||||
auto it = document_store.find(uri);
|
||||
return (it != document_store.end()) ? it->second : "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include "../base/provider_interface.hpp"
|
||||
|
||||
namespace lsp::providers::text_document
|
||||
{
|
||||
class DidOpenProvider : public ILspProvider
|
||||
{
|
||||
public:
|
||||
DidOpenProvider() = default;
|
||||
nlohmann::json ProvideResponse(const LspRequest& request) override;
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
|
||||
// 静态方法用于获取文档内容
|
||||
static std::string GetDocumentContent(const std::string& uri);
|
||||
|
||||
public:
|
||||
static std::unordered_map<std::string, std::string> document_store;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "./set_trace_provider.hpp"
|
||||
|
||||
namespace lsp::providers::trace
|
||||
{
|
||||
|
||||
nlohmann::json SetTraceProvider::ProvideResponse(const LspRequest& request)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto params = request.params;
|
||||
if (params.contains("value"))
|
||||
{
|
||||
std::string trace_value = params["value"];
|
||||
// 这里可以设置跟踪级别
|
||||
// 例如:设置全局跟踪变量
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// 处理错误
|
||||
}
|
||||
|
||||
// 通知不需要响应
|
||||
return nlohmann::json();
|
||||
}
|
||||
|
||||
inline std::string SetTraceProvider::GetMethod() const
|
||||
{
|
||||
return "$/setTrace";
|
||||
}
|
||||
|
||||
inline std::string SetTraceProvider::GetProviderName() const
|
||||
{
|
||||
return "SetTraceProvider";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "../base/provider_interface.hpp"
|
||||
|
||||
namespace lsp::providers::trace
|
||||
{
|
||||
class SetTraceProvider : public ILspProvider
|
||||
{
|
||||
public:
|
||||
SetTraceProvider() = default;
|
||||
nlohmann::json ProvideResponse(const LspRequest& request) override;
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user