支持全局函数补全
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
#include "./keyword_manager.hpp"
|
||||
|
||||
namespace tsl
|
||||
{
|
||||
KeywordManager& KeywordManager::GetInstance()
|
||||
{
|
||||
static KeywordManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
KeywordManager::KeywordManager()
|
||||
{
|
||||
InitKeywords();
|
||||
}
|
||||
|
||||
std::vector<KeywordInfo> KeywordManager::GetAllKeywords()
|
||||
{
|
||||
std::vector<KeywordInfo> keyword;
|
||||
keyword.reserve(keywords_.size());
|
||||
for (const auto& [key, value] : keywords_)
|
||||
keyword.push_back(value);
|
||||
return keyword;
|
||||
}
|
||||
|
||||
std::vector<KeywordInfo> KeywordManager::GetKeyWordByPrefix(std::string prefix)
|
||||
{
|
||||
std::vector<KeywordInfo> keyword;
|
||||
for (const auto& [key, value] : keywords_)
|
||||
{
|
||||
if (key.starts_with(prefix))
|
||||
keyword.push_back(value);
|
||||
}
|
||||
return keyword;
|
||||
}
|
||||
|
||||
std::optional<KeywordInfo> KeywordManager::GetKeywordInfo(std::string key)
|
||||
{
|
||||
auto target = keywords_.find(key);
|
||||
if (target == keywords_.end())
|
||||
return std::nullopt;
|
||||
return target->second;
|
||||
}
|
||||
|
||||
void KeywordManager::InitKeywords()
|
||||
{
|
||||
keywords_.reserve(200);
|
||||
|
||||
InitProgramStructureKeywords();
|
||||
InitDataTypeKeywords();
|
||||
InitClassKeywords();
|
||||
InitControlFlowKeywords();
|
||||
InitOperatorKeywords();
|
||||
InitSqlKeywords();
|
||||
InitBuiltinKeywords();
|
||||
InitConstantKeywords();
|
||||
}
|
||||
|
||||
void KeywordManager::InitProgramStructureKeywords()
|
||||
{
|
||||
keywords_["program"] = {
|
||||
"program",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Program\n\n程序开始的入口,一般用户不需要使用,在作为独立的TSL脚本时可以使用。 \n通常来说,倘若编写TSL代码作为CGI执行运行,系统默认以PROGRAM模式运行。虽然TSL可以省略PROGRAM关键字,但是使用PROGRAM关键字可以使得TSL代码里包含子函数.\n\n**例如:**\n```pascal\nprogram Test;\n\tfunction sub1();\n\tbegin\n\t\twriteln('Execute sub1');\n\tend;\nbegin\n\tsub1();\nend.\n```"
|
||||
};
|
||||
|
||||
keywords_["function"] = {
|
||||
"function",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Function\n\n函数声明开始,组成类似于FUNCTION XXXX(); BEGIN END;的函数块"
|
||||
};
|
||||
|
||||
keywords_["procedure"] = {
|
||||
"procedure",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Procedure\n\n与FUNCTION类似,但是在函数头后不允许加返回类型值"
|
||||
};
|
||||
|
||||
keywords_["unit"] = {
|
||||
"unit",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Unit Declaration\n\nDeclares a code unit/module.\n\n**Syntax:**\n```pascal\nunit UnitName;\ninterface\n // public declarations\nimplementation\n // private implementation\nend.\n```"
|
||||
};
|
||||
|
||||
keywords_["uses"] = {
|
||||
"uses",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Uses Clause\n\nImports external units/modules.\n\n**Syntax:**\n```pascal\nuses Unit1, Unit2, Unit3;\n```\n\n**Example:**\n```pascal\nuses System, SysUtils, Classes;\n```"
|
||||
};
|
||||
|
||||
keywords_["implementation"] = {
|
||||
"implementation",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Implementation Section\n\nMarks the beginning of the private implementation section in a unit.\n\n**Usage:**\n```pascal\nunit MyUnit;\ninterface\n // public interface\nimplementation\n // private implementation\nend.\n```"
|
||||
};
|
||||
|
||||
keywords_["interface"] = {
|
||||
"interface",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Interface Section\n\nMarks the public interface section in a unit.\n\n**Usage:**\n```pascal\nunit MyUnit;\ninterface\n // public declarations\n function PublicFunction: integer;\nimplementation\nend.\n```"
|
||||
};
|
||||
|
||||
keywords_["initialization"] = {
|
||||
"initialization",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Initialization Section\n\nCode executed when the unit is first loaded.\n\n**Usage:**\n```pascal\nunit MyUnit;\ninterface\nimplementation\ninitialization\n // initialization code\nend.\n```"
|
||||
};
|
||||
|
||||
keywords_["finalization"] = {
|
||||
"finalization",
|
||||
KeywordCategory::kProgramStructure,
|
||||
"## Finalization Section\n\nCode executed when the program terminates.\n\n**Usage:**\n```pascal\nunit MyUnit;\ninterface\nimplementation\ninitialization\n // setup code\nfinalization\n // cleanup code\nend.\n```"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitDataTypeKeywords()
|
||||
{
|
||||
keywords_["string"] = {
|
||||
"string",
|
||||
KeywordCategory::kDataTypes,
|
||||
"## String Type\n\nVariable-length string data type.\n\n**Example:**\n```pascal\nvar\n myString: string;\nbegin\n myString := 'Hello World';\nend;\n```\n\n**Note:** Supports Unicode and automatic memory management."
|
||||
};
|
||||
|
||||
keywords_["integer"] = {
|
||||
"integer",
|
||||
KeywordCategory::kDataTypes,
|
||||
"## Integer Type\n\n32-bit signed integer data type.\n\n**Range:** -2,147,483,648 to 2,147,483,647\n\n**Example:**\n```pascal\nvar\n count: integer;\nbegin\n count := 42;\nend;\n```"
|
||||
};
|
||||
|
||||
keywords_["boolean"] = {
|
||||
"boolean",
|
||||
KeywordCategory::kDataTypes,
|
||||
"## Boolean Type\n\nLogical data type with values `true` or `false`.\n\n**Example:**\n```pascal\nvar\n isValid: boolean;\nbegin\n isValid := true;\n if isValid then\n echo('Valid!');\nend;\n```"
|
||||
};
|
||||
|
||||
keywords_["int64"] = {
|
||||
"int64",
|
||||
KeywordCategory::kDataTypes,
|
||||
"## Int64 Type\n\n64-bit signed integer data type.\n\n**Range:** -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807\n\n**Example:**\n```pascal\nvar\n bigNumber: int64;\nbegin\n bigNumber := 1234567890123456789;\nend;\n```"
|
||||
};
|
||||
|
||||
keywords_["real"] = {
|
||||
"real",
|
||||
KeywordCategory::kDataTypes,
|
||||
"## Real Type\n\nFloating-point number data type.\n\n**Example:**\n```pascal\nvar\n price: real;\nbegin\n price := 19.99;\nend;\n```"
|
||||
};
|
||||
|
||||
keywords_["array"] = {
|
||||
"array",
|
||||
KeywordCategory::kDataTypes,
|
||||
"## Array Type\n\nCollection of elements of the same type.\n\n**Syntax:**\n```pascal\narray[IndexType] of ElementType\n```\n\n**Examples:**\n```pascal\nvar\n numbers: array[1..10] of integer;\n names: array of string; // dynamic array\nbegin\n numbers[1] := 42;\nend;\n```"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitClassKeywords()
|
||||
{
|
||||
keywords_["type"] = {
|
||||
"type",
|
||||
KeywordCategory::kClassTypes,
|
||||
"## Type Declaration\n\nDeclares custom data types.\n\n**Syntax:**\n```pascal\ntype\n TypeName = TypeDefinition;\n```\n\n**Example:**\n```pascal\ntype\n TPoint = record\n X, Y: integer;\n end;\n \n TMyClass = class\n // class members\n end;\n```"
|
||||
};
|
||||
|
||||
keywords_["class"] = {
|
||||
"class",
|
||||
KeywordCategory::kClassTypes,
|
||||
"## Class Declaration\n\nDeclares an object-oriented class.\n\n**Syntax:**\n```pascal\ntype\n TClassName = class(TBaseClass)\n private\n // private members\n public\n // public members\n end;\n```\n\n**Example:**\n```pascal\ntype\n TPerson = class\n private\n FName: string;\n public\n constructor Create(AName: string);\n property Name: string read FName write FName;\n end;\n```"
|
||||
};
|
||||
|
||||
keywords_["new"] = {
|
||||
"new",
|
||||
KeywordCategory::kClassTypes,
|
||||
"## New Instance\n\nCreates a new instance of a class.\n\n**Syntax:**\n```pascal\nInstanceVar := ClassName.Create();\n```\n\n**Example:**\n```pascal\nvar\n person: TPerson;\nbegin\n person := TPerson.Create('John');\n try\n // use person\n finally\n person.Free;\n end;\nend;\n```"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitControlFlowKeywords()
|
||||
{
|
||||
keywords_["if"] = {
|
||||
"if",
|
||||
KeywordCategory::kConditionals,
|
||||
"## If Statement\n\nConditional execution based on a boolean expression.\n\n**Syntax:**\n```pascal\nif condition then\n statement\nelse\n statement;\n```\n\n**Example:**\n```pascal\nif age >= 18 then\n echo('Adult')\nelse\n echo('Minor');\n```\n\n**Multi-line:**\n```pascal\nif score >= 90 then\nbegin\n grade := 'A';\n echo('Excellent!');\nend;\n```"
|
||||
};
|
||||
|
||||
keywords_["for"] = {
|
||||
"for",
|
||||
KeywordCategory::kLoops,
|
||||
"## For Loop\n\nIterates over a range of values.\n\n**Syntax:**\n```pascal\nfor variable := startValue to endValue do\n statement;\n \nfor variable := startValue downto endValue do\n statement;\n```\n\n**Examples:**\n```pascal\n// Forward loop\nfor i := 1 to 10 do\n echo(i);\n \n// Reverse loop\nfor i := 10 downto 1 do\n echo(i);\n \n// Array iteration\nfor i := Low(myArray) to High(myArray) do\n echo(myArray[i]);\n```"
|
||||
};
|
||||
|
||||
keywords_["while"] = {
|
||||
"while",
|
||||
KeywordCategory::kLoops,
|
||||
"## While Loop\n\nRepeats while a condition is true.\n\n**Syntax:**\n```pascal\nwhile condition do\n statement;\n```\n\n**Example:**\n```pascal\ni := 0;\nwhile i < 10 do\nbegin\n echo(i);\n i := i + 1;\nend;\n```\n\n**Note:** The condition is checked before each iteration."
|
||||
};
|
||||
|
||||
keywords_["case"] = {
|
||||
"case",
|
||||
KeywordCategory::kConditionals,
|
||||
"## Case Statement\n\nMulti-way conditional based on value matching.\n\n**Syntax:**\n```pascal\ncase expression of\n value1: statement1;\n value2: statement2;\n else\n defaultStatement;\nend;\n```\n\n**Example:**\n```pascal\ncase dayOfWeek of\n 1: echo('Monday');\n 2: echo('Tuesday');\n 3: echo('Wednesday');\n 4: echo('Thursday');\n 5: echo('Friday');\n 6, 7: echo('Weekend');\n else\n echo('Invalid day');\nend;\n```"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitOperatorKeywords()
|
||||
{
|
||||
keywords_["and"] = {
|
||||
"and",
|
||||
KeywordCategory::kLogicalOperators,
|
||||
"## Logical AND\n\nLogical conjunction operator.\n\n**Truth Table:**\n| A | B | A and B |\n|---|---|--------|\n| T | T | T |\n| T | F | F |\n| F | T | F |\n| F | F | F |\n\n**Example:**\n```pascal\nif (age >= 18) and (hasLicense) then\n echo('Can drive');\n```"
|
||||
};
|
||||
|
||||
keywords_["or"] = {
|
||||
"or",
|
||||
KeywordCategory::kLogicalOperators,
|
||||
"## Logical OR\n\nLogical disjunction operator.\n\n**Truth Table:**\n| A | B | A or B |\n|---|---|-------|\n| T | T | T |\n| T | F | T |\n| F | T | T |\n| F | F | F |\n\n**Example:**\n```pascal\nif (isAdmin) or (isOwner) then\n echo('Has permission');\n```"
|
||||
};
|
||||
|
||||
keywords_["div"] = {
|
||||
"div",
|
||||
KeywordCategory::kArithmeticOperators,
|
||||
"## Integer Division\n\nPerforms integer division (truncates decimal part).\n\n**Example:**\n```pascal\nvar result: integer;\nbegin\n result := 17 div 5; // result = 3\n result := 20 div 4; // result = 5\nend;\n```\n\n**Note:** Use `/` for real division, `div` for integer division."
|
||||
};
|
||||
|
||||
keywords_["mod"] = {
|
||||
"mod",
|
||||
KeywordCategory::kArithmeticOperators,
|
||||
"## Modulo Operator\n\nReturns the remainder of integer division.\n\n**Example:**\n```pascal\nvar remainder: integer;\nbegin\n remainder := 17 mod 5; // remainder = 2\n remainder := 20 mod 4; // remainder = 0\n \n // Check if number is even\n if (number mod 2) = 0 then\n echo('Even number');\nend;\n```"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitSqlKeywords()
|
||||
{
|
||||
keywords_["select"] = {
|
||||
"select",
|
||||
KeywordCategory::kSqlControl,
|
||||
"## SQL SELECT Statement\n\nRetrieves data from database tables.\n\n**Syntax:**\n```sql\nSELECT column1, column2, ...\nFROM table_name\nWHERE condition\nORDER BY column;\n```\n\n**Examples:**\n```sql\n-- Basic select\nSELECT name, age FROM users;\n\n-- With condition\nSELECT * FROM products WHERE price > 100;\n\n-- With ordering\nSELECT name, salary FROM employees ORDER BY salary DESC;\n```"
|
||||
};
|
||||
|
||||
keywords_["update"] = {
|
||||
"update",
|
||||
KeywordCategory::kSqlControl,
|
||||
"## SQL UPDATE Statement\n\nModifies existing records in a table.\n\n**Syntax:**\n```sql\nUPDATE table_name\nSET column1 = value1, column2 = value2, ...\nWHERE condition;\n```\n\n**Examples:**\n```sql\n-- Update single record\nUPDATE users SET age = 30 WHERE id = 1;\n\n-- Update multiple columns\nUPDATE products \nSET price = 99.99, category = 'Electronics'\nWHERE id = 100;\n```\n\n**⚠️ Warning:** Always use WHERE clause to avoid updating all records!"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitBuiltinKeywords()
|
||||
{
|
||||
keywords_["echo"] = {
|
||||
"echo",
|
||||
KeywordCategory::kBuiltinFunctions,
|
||||
"## Echo Function\n\nOutputs text to the console or output stream.\n\n**Syntax:**\n```pascal\necho(expression);\necho(format, args...);\n```\n\n**Examples:**\n```pascal\necho('Hello World!');\necho('Number: ', 42);\necho('Name: %s, Age: %d', name, age);\n```\n\n**Features:**\n- Supports multiple arguments\n- Automatic type conversion\n- Format string support"
|
||||
};
|
||||
|
||||
keywords_["mtic"] = {
|
||||
"mtic",
|
||||
KeywordCategory::kBuiltinFunctions,
|
||||
"## Timer Start (mtic)\n\nStarts a high-precision timer for performance measurement.\n\n**Usage:**\n```pascal\nmtic(); // Start timer\n// ... code to measure ...\nvar elapsed := mtoc(); // Get elapsed time\necho('Elapsed: ', elapsed, ' seconds');\n```\n\n**Note:** Use with `mtoc()` to measure execution time."
|
||||
};
|
||||
|
||||
keywords_["mtoc"] = {
|
||||
"mtoc",
|
||||
KeywordCategory::kBuiltinFunctions,
|
||||
"## Timer End (mtoc)\n\nReturns elapsed time since last `mtic()` call.\n\n**Returns:** Time in seconds (real number)\n\n**Example:**\n```pascal\nmtic();\nfor i := 1 to 1000000 do\n // some computation\nvar timeElapsed := mtoc();\necho('Loop took: ', timeElapsed, ' seconds');\n```"
|
||||
};
|
||||
}
|
||||
|
||||
void KeywordManager::InitConstantKeywords()
|
||||
{
|
||||
keywords_["true"] = {
|
||||
"true",
|
||||
KeywordCategory::kBooleanConstants,
|
||||
"## Boolean True\n\nRepresents the boolean value **true**.\n\n**Usage:**\n```pascal\nvar\n flag: boolean;\nbegin\n flag := true;\n \n if flag then\n echo('Flag is set!');\nend;\n```\n\n**Note:** Case-insensitive in most Pascal dialects."
|
||||
};
|
||||
|
||||
keywords_["false"] = {
|
||||
"false",
|
||||
KeywordCategory::kBooleanConstants,
|
||||
"## Boolean False\n\nRepresents the boolean value **false**.\n\n**Usage:**\n```pascal\nvar\n isComplete: boolean;\nbegin\n isComplete := false;\n \n while not isComplete do\n begin\n // do work\n isComplete := checkCompletion();\n end;\nend;\n```"
|
||||
};
|
||||
|
||||
keywords_["nil"] = {
|
||||
"nil",
|
||||
KeywordCategory::kNullConstants,
|
||||
"## Nil Constant\n\nRepresents a null/empty pointer or object reference.\n\n**Usage:**\n```pascal\nvar\n obj: TMyClass;\nbegin\n obj := nil; // Initialize to null\n \n if obj <> nil then\n obj.DoSomething();\n \n obj := TMyClass.Create();\n try\n // use obj\n finally\n obj.Free;\n obj := nil; // Clear reference\n end;\nend;\n```\n\n**Best Practice:** Always check for nil before using object references."
|
||||
};
|
||||
|
||||
keywords_["inf"] = {
|
||||
"inf",
|
||||
KeywordCategory::kMathConstants,
|
||||
"## Infinity Constant\n\nRepresents positive mathematical infinity.\n\n**Usage:**\n```pascal\nvar\n result: real;\nbegin\n result := 1.0 / 0.0; // Results in inf\n \n if result = inf then\n echo('Result is infinite');\nend;\n```\n\n**Note:** Use for floating-point calculations and comparisons."
|
||||
};
|
||||
|
||||
keywords_["nan"] = {
|
||||
"nan",
|
||||
KeywordCategory::kMathConstants,
|
||||
"## Not a Number (NaN)\n\nRepresents an undefined or invalid floating-point result.\n\n**Common Causes:**\n- `0.0 / 0.0`\n- `sqrt(-1.0)`\n- `inf - inf`\n\n**Usage:**\n```pascal\nvar\n result: real;\nbegin\n result := sqrt(-1.0); // Results in NaN\n \n if IsNaN(result) then\n echo('Invalid calculation');\nend;\n```\n\n**Note:** NaN ≠ NaN, use `IsNaN()` function for checking."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "./keyword_types.hpp"
|
||||
|
||||
namespace tsl
|
||||
{
|
||||
class KeywordManager
|
||||
{
|
||||
public:
|
||||
KeywordManager(const KeywordManager&) = delete;
|
||||
KeywordManager& operator=(const KeywordManager&) = delete;
|
||||
|
||||
static KeywordManager& GetInstance();
|
||||
std::vector<KeywordInfo> GetAllKeywords();
|
||||
std::vector<KeywordInfo> GetKeyWordByPrefix(std::string prefix);
|
||||
std::optional<KeywordInfo> GetKeywordInfo(std::string key);
|
||||
|
||||
private:
|
||||
KeywordManager();
|
||||
void InitKeywords();
|
||||
|
||||
void InitProgramStructureKeywords();
|
||||
void InitDataTypeKeywords();
|
||||
void InitClassKeywords();
|
||||
void InitControlFlowKeywords();
|
||||
void InitOperatorKeywords();
|
||||
void InitSqlKeywords();
|
||||
void InitBuiltinKeywords();
|
||||
void InitConstantKeywords();
|
||||
|
||||
std::unordered_map<std::string, KeywordInfo> keywords_;
|
||||
};
|
||||
}
|
||||
+49
-71
@@ -1,71 +1,49 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include "../protocol/protocol.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::protocol::CompletionItemKind completion_kind;
|
||||
};
|
||||
|
||||
class TslKeywords
|
||||
{
|
||||
public:
|
||||
static const std::vector<KeywordInfo>& GetAllKeywords();
|
||||
static std::vector<KeywordInfo> GetKeywordsByCategory(KeywordCategory category);
|
||||
static std::vector<lsp::protocol::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_;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "../protocol/protocol.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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
#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::protocol::CompletionItem> TslKeywords::GetCompletionItems(const std::string& prefix)
|
||||
{
|
||||
InitKeywords();
|
||||
std::vector<lsp::protocol::CompletionItem> result;
|
||||
std::string lower_prefix = ToLowerCase(prefix);
|
||||
|
||||
for (const auto& keyword : keywords_)
|
||||
{
|
||||
if (prefix.empty() || StartsWith(ToLowerCase(keyword.keyword), lower_prefix))
|
||||
{
|
||||
lsp::protocol::CompletionItem item;
|
||||
item.label = keyword.keyword;
|
||||
item.kind = keyword.completion_kind;
|
||||
item.detail = keyword.description;
|
||||
item.insertText = keyword.keyword;
|
||||
result.push_back(item);
|
||||
}
|
||||
}
|
||||
// 按字母顺序排序
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const lsp::protocol::CompletionItem& a, const lsp::protocol::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::protocol::CompletionItemKind::kKeyword },
|
||||
{ "function", KeywordCategory::kProgramStructure, "Function declaration", lsp::protocol::CompletionItemKind::kFunction },
|
||||
{ "procedure", KeywordCategory::kProgramStructure, "Procedure declaration", lsp::protocol::CompletionItemKind::kFunction },
|
||||
{ "unit", KeywordCategory::kProgramStructure, "Unit declaration", lsp::protocol::CompletionItemKind::kModule },
|
||||
{ "uses", KeywordCategory::kProgramStructure, "Uses clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "implementation", KeywordCategory::kProgramStructure, "Implementation section", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "interface", KeywordCategory::kProgramStructure, "Interface section", lsp::protocol::CompletionItemKind::kInterface },
|
||||
{ "initialization", KeywordCategory::kProgramStructure, "Initialization section", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "finalization", KeywordCategory::kProgramStructure, "Finalization section", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Data Types
|
||||
{ "string", KeywordCategory::kDataTypes, "String data type", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "integer", KeywordCategory::kDataTypes, "Integer data type", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "boolean", KeywordCategory::kDataTypes, "Boolean data type", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "int64", KeywordCategory::kDataTypes, "64-bit integer data type", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "real", KeywordCategory::kDataTypes, "Real number data type", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "array", KeywordCategory::kDataTypes, "Array data type", lsp::protocol::CompletionItemKind::kClass },
|
||||
|
||||
// Class Types
|
||||
{ "type", KeywordCategory::kClassTypes, "Type declaration", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "class", KeywordCategory::kClassTypes, "Class declaration", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "fakeclass", KeywordCategory::kClassTypes, "Fake class declaration", lsp::protocol::CompletionItemKind::kClass },
|
||||
{ "new", KeywordCategory::kClassTypes, "Object instantiation", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Class Modifiers
|
||||
{ "override", KeywordCategory::kClassModifiers, "Override method", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "overload", KeywordCategory::kClassModifiers, "Overload method", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "virtual", KeywordCategory::kClassModifiers, "Virtual method", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "property", KeywordCategory::kClassModifiers, "Property declaration", lsp::protocol::CompletionItemKind::kProperty },
|
||||
{ "self", KeywordCategory::kClassModifiers, "Self reference", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "inherited", KeywordCategory::kClassModifiers, "Inherited method call", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Access Modifiers
|
||||
{ "public", KeywordCategory::kAccessModifiers, "Public access modifier", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "protected", KeywordCategory::kAccessModifiers, "Protected access modifier", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "private", KeywordCategory::kAccessModifiers, "Private access modifier", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "published", KeywordCategory::kAccessModifiers, "Published access modifier", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Property Accessors
|
||||
{ "read", KeywordCategory::kPropertyAccessors, "Property read accessor", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "write", KeywordCategory::kPropertyAccessors, "Property write accessor", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Constructors
|
||||
{ "create", KeywordCategory::kConstructors, "Constructor method", lsp::protocol::CompletionItemKind::kConstructor },
|
||||
{ "destroy", KeywordCategory::kConstructors, "Destructor method", lsp::protocol::CompletionItemKind::kMethod },
|
||||
{ "operator", KeywordCategory::kConstructors, "Operator overload", lsp::protocol::CompletionItemKind::kOperator },
|
||||
|
||||
// Variable Modifiers
|
||||
{ "external", KeywordCategory::kVariableModifiers, "External declaration", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "const", KeywordCategory::kVariableModifiers, "Constant declaration", lsp::protocol::CompletionItemKind::kConstant },
|
||||
{ "out", KeywordCategory::kVariableModifiers, "Output parameter", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "var", KeywordCategory::kVariableModifiers, "Variable declaration", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "global", KeywordCategory::kVariableModifiers, "Global variable", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "static", KeywordCategory::kVariableModifiers, "Static variable", lsp::protocol::CompletionItemKind::kVariable },
|
||||
|
||||
// Conditionals
|
||||
{ "if", KeywordCategory::kConditionals, "If statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "else", KeywordCategory::kConditionals, "Else clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "then", KeywordCategory::kConditionals, "Then clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "case", KeywordCategory::kConditionals, "Case statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "of", KeywordCategory::kConditionals, "Of clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Loops
|
||||
{ "for", KeywordCategory::kLoops, "For loop", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "while", KeywordCategory::kLoops, "While loop", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "do", KeywordCategory::kLoops, "Do clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "downto", KeywordCategory::kLoops, "Downto clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "step", KeywordCategory::kLoops, "Step clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "until", KeywordCategory::kLoops, "Until clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "repeat", KeywordCategory::kLoops, "Repeat loop", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "to", KeywordCategory::kLoops, "To clause", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Branch Control
|
||||
{ "break", KeywordCategory::kBranchControl, "Break statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "continue", KeywordCategory::kBranchControl, "Continue statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "goto", KeywordCategory::kBranchControl, "Goto statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "label", KeywordCategory::kBranchControl, "Label declaration", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "exit", KeywordCategory::kBranchControl, "Exit statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Return Control
|
||||
{ "return", KeywordCategory::kReturnControl, "Return statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "debugreturn", KeywordCategory::kReturnControl, "Debug return statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "debugrunenv", KeywordCategory::kReturnControl, "Debug run environment", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "debugrunenvdo", KeywordCategory::kReturnControl, "Debug run environment do", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Block Control
|
||||
{ "begin", KeywordCategory::kBlockControl, "Begin block", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "end", KeywordCategory::kBlockControl, "End block", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "with", KeywordCategory::kBlockControl, "With statement", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// References
|
||||
{ "weakref", KeywordCategory::kReferences, "Weak reference", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "autoref", KeywordCategory::kReferences, "Auto reference", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Namespace
|
||||
{ "namespace", KeywordCategory::kNamespace, "Namespace declaration", lsp::protocol::CompletionItemKind::kModule },
|
||||
|
||||
// Exceptions
|
||||
{ "except", KeywordCategory::kExceptions, "Exception handling", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "raise", KeywordCategory::kExceptions, "Raise exception", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "try", KeywordCategory::kExceptions, "Try block", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "finally", KeywordCategory::kExceptions, "Finally block", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "exceptobject", KeywordCategory::kExceptions, "Exception object", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Logical Operators
|
||||
{ "and", KeywordCategory::kLogicalOperators, "Logical AND", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "in", KeywordCategory::kLogicalOperators, "In operator", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "is", KeywordCategory::kLogicalOperators, "Is operator", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "not", KeywordCategory::kLogicalOperators, "Logical NOT", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "or", KeywordCategory::kLogicalOperators, "Logical OR", lsp::protocol::CompletionItemKind::kOperator },
|
||||
|
||||
// Arithmetic Operators
|
||||
{ "div", KeywordCategory::kArithmeticOperators, "Integer division", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "mod", KeywordCategory::kArithmeticOperators, "Modulo operation", lsp::protocol::CompletionItemKind::kOperator },
|
||||
|
||||
// Bitwise Operators
|
||||
{ "ror", KeywordCategory::kBitwiseOperators, "Rotate right", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "rol", KeywordCategory::kBitwiseOperators, "Rotate left", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "shr", KeywordCategory::kBitwiseOperators, "Shift right", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "shl", KeywordCategory::kBitwiseOperators, "Shift left", lsp::protocol::CompletionItemKind::kOperator },
|
||||
|
||||
// Set Operators
|
||||
{ "union", KeywordCategory::kSetOperators, "Set union", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "minus", KeywordCategory::kSetOperators, "Set difference", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "union2", KeywordCategory::kSetOperators, "Set union (alternative)", lsp::protocol::CompletionItemKind::kOperator },
|
||||
|
||||
// SQL Control
|
||||
{ "select", KeywordCategory::kSqlControl, "SQL SELECT", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "vselect", KeywordCategory::kSqlControl, "Virtual SELECT", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "sselect", KeywordCategory::kSqlControl, "Special SELECT", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "update", KeywordCategory::kSqlControl, "SQL UPDATE", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "delete", KeywordCategory::kSqlControl, "SQL DELETE", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "mselect", KeywordCategory::kSqlControl, "Multiple SELECT", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// SQL Keywords
|
||||
{ "sqlin", KeywordCategory::kSqlKeywords, "SQL IN", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "from", KeywordCategory::kSqlKeywords, "SQL FROM", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "where", KeywordCategory::kSqlKeywords, "SQL WHERE", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "group", KeywordCategory::kSqlKeywords, "SQL GROUP", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "by", KeywordCategory::kSqlKeywords, "SQL BY", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "order", KeywordCategory::kSqlKeywords, "SQL ORDER", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "distinct", KeywordCategory::kSqlKeywords, "SQL DISTINCT", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "join", KeywordCategory::kSqlKeywords, "SQL JOIN", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// SQL Operators (note: some overlap with logical operators)
|
||||
{ "on", KeywordCategory::kSqlOperators, "SQL ON", lsp::protocol::CompletionItemKind::kOperator },
|
||||
{ "like", KeywordCategory::kSqlOperators, "SQL LIKE", lsp::protocol::CompletionItemKind::kOperator },
|
||||
|
||||
// Calling Conventions
|
||||
{ "cdecl", KeywordCategory::kCallingConventions, "C calling convention", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "pascal", KeywordCategory::kCallingConventions, "Pascal calling convention", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "stdcall", KeywordCategory::kCallingConventions, "Standard calling convention", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "safecall", KeywordCategory::kCallingConventions, "Safe calling convention", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "fastcall", KeywordCategory::kCallingConventions, "Fast calling convention", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "register", KeywordCategory::kCallingConventions, "Register calling convention", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// System Keywords
|
||||
{ "setuid", KeywordCategory::kSystemKeywords, "Set user ID", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
{ "sudo", KeywordCategory::kSystemKeywords, "Super user do", lsp::protocol::CompletionItemKind::kKeyword },
|
||||
|
||||
// Builtin Variables
|
||||
{ "paramcount", KeywordCategory::kBuiltinVariables, "Parameter count", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "realparamcount", KeywordCategory::kBuiltinVariables, "Real parameter count", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "params", KeywordCategory::kBuiltinVariables, "Parameters array", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "system", KeywordCategory::kBuiltinVariables, "System variable", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "tslassigning", KeywordCategory::kBuiltinVariables, "TSL assigning", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "likeeps", KeywordCategory::kBuiltinVariables, "Like EPS", lsp::protocol::CompletionItemKind::kVariable },
|
||||
{ "likeepsrate", KeywordCategory::kBuiltinVariables, "Like EPS rate", lsp::protocol::CompletionItemKind::kVariable },
|
||||
|
||||
// Builtin Functions
|
||||
{ "echo", KeywordCategory::kBuiltinFunctions, "Echo function", lsp::protocol::CompletionItemKind::kFunction },
|
||||
{ "mtic", KeywordCategory::kBuiltinFunctions, "MTIC function", lsp::protocol::CompletionItemKind::kFunction },
|
||||
{ "mtoc", KeywordCategory::kBuiltinFunctions, "MTOC function", lsp::protocol::CompletionItemKind::kFunction },
|
||||
|
||||
// Boolean Constants
|
||||
{ "false", KeywordCategory::kBooleanConstants, "Boolean false", lsp::protocol::CompletionItemKind::kConstant },
|
||||
{ "true", KeywordCategory::kBooleanConstants, "Boolean true", lsp::protocol::CompletionItemKind::kConstant },
|
||||
|
||||
// Math Constants
|
||||
{ "inf", KeywordCategory::kMathConstants, "Infinity", lsp::protocol::CompletionItemKind::kConstant },
|
||||
{ "nan", KeywordCategory::kMathConstants, "Not a Number", lsp::protocol::CompletionItemKind::kConstant },
|
||||
|
||||
// Null Constants
|
||||
{ "nil", KeywordCategory::kNullConstants, "Null value", lsp::protocol::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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user