commit 40634c2fda5bc53a205722983d85f961be870dc1 Author: csh Date: Sat Jun 14 15:36:19 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa36576 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vscode/tsl-syntax/node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..570d032 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# TSL Syntax + +该插件提供 tsl 语言的语法高亮 + +## VSCode + +### 安装方式一 + +复制[tsl-syntax](./vscode/tsl-syntax/)文件夹到`VSCode`扩展目录 + +```txt +Windows: %USERPROFILE%\.vscode\extensions +macOS: ~/.vscode/extensions +Linux: ~/.vscode/extensions +``` + +### 安装方式二 + +安装[vsix](./vscode/tsl-syntax/tsl-syntax-1.0.0.vsix)扩展 + +```txt +code --install-extension tsl-syntax-1.0.0.vsix +``` + +## Vim + +将[tsl.vim](./vim/tsl.vim)放入`~/.vim/syntax/` + +```vim +" 你的vimrc加入,可同时支持tsl和tsf +autocmd BufNewFile,BufRead *.ts[lf] setf tsl +``` diff --git a/test.tsl b/test.tsl new file mode 100644 index 0000000..1015dec --- /dev/null +++ b/test.tsl @@ -0,0 +1,35 @@ +type MyClass = class +type UserType = class +end; + +MyClass.init() +UserType.getName(): string; +function MyClass.setValue(val: integer = 123; a: stringa): AA; +begin +end + +function Mycalss.Init(); +begin +end; + +obj.name; +user.profile.email; +data.items.count; +data.Func(a: abc; b: 2); + +function f(); // 无参数 +begin + {self.}abc; +end; +function f(a, b); // 参数无类型 +begin +end; +function f(a: real; b: DD); // 参数有类型 +function f(a: real; b): def; // 带返回类型 +begin +end; +a ? b : c; + +f(); +f(a, b); +f(a: 1; b: 's'); diff --git a/vim/tsl.vim b/vim/tsl.vim new file mode 100644 index 0000000..7737fdb --- /dev/null +++ b/vim/tsl.vim @@ -0,0 +1,184 @@ +vim9script + +# Vim9 syntax file for TSL language +if exists("b:current_syntax") + finish +endif + +var cpo_save = &cpo +set cpo&vim + +syn case ignore + +# Keywords +syn keyword tslHeader program function procedure nextgroup=tslFuncName skipwhite +syn keyword tslBuiltinVar paramcount realparamcount params +syn keyword tslBuiltinVar system thisfunction tslassigning +syn keyword tslBuiltinVar likeeps likeepsrate + +syn keyword tslConditional else if +syn keyword tslStatement begin end then +syn keyword tslStatement this with exit +syn keyword tslStatement weakref autoref namespace +syn keyword tslInterface unit uses implementation interface initalization finalization +syn keyword tslRepeat for while do downto step until repeat to +syn keyword tslBranch break continue +syn keyword tslReturn return debugreturn debugrunenv debugrunenvdo +syn keyword tslLabel case of goto label + +syn keyword tslOperator write read +syn keyword tslOperator union minus union2 +syn keyword tslShiftOperator ror rol shr shl +syn keyword tslLogicOperator and in is not or +syn keyword tslArithmOperator div mod + +syn keyword tslException except raise try finally exceptobject +syn keyword tslBoolean false true +syn keyword tslNil nil + +syn keyword tslType string integer boolean int64 real +syn keyword tslBuiltins echo mtic mtoc this +syn keyword tslBuiltins inf nan +syn keyword tslBuiltins external const out var + +syn keyword tslSql select vselect sselect update delete mselect set +syn keyword tslSqlOperator sqlin from where group by like order + +syn keyword tslOther setuid sudo +syn keyword tslCallFunc cdecl pascal stdcall safecall fastcall register +syn keyword tslScope global static +syn keyword tslClass type class fakeclass new +syn keyword tslClassModifier override overload virtual property self inherited +syn keyword tslConstruct create destroy operator +syn keyword tslAccess public protected private published +syn keyword tslTodo FIXME NOTE NOTES TODO XXX contained + +# Function definitions +syn match tslFuncName '\%(\h\w*\.\)\?\h\w*' contained nextgroup=tslFuncParams skipwhite contains=tslTypeNameInFunc,tslDotInFunc,tslFuncNamePart +syn match tslTypeNameInFunc '\h\w*\ze\.\h\w*' contained +syn match tslDotInFunc '\.' contained +syn match tslFuncName '\.\@<=\h\w*' contained + +syn region tslFuncParams + \ start='(' + \ end=')' + \ contained + \ contains=tslParam,tslParamType,tslParamSep + \ nextgroup=tslReturnType skipwhite + +syn match tslParam '\h\w*' contained nextgroup=tslParamType skipnl skipwhite +syn match tslParamType ':\s*\h\w*' contained contains=tslColon,tslTypeName +syn match tslReturnType ':\s*\h\w*' contained contains=tslColon,tslTypeName +syn match tslTypeName '\h\w*' contained +syn match tslParamSep '[;,]' contained +syn match tslColon ':' contained + +# 匹配完整的属性链 +syn match tslPropertyChain '\h\w*\%(\.\h\w*\)\+' contains=tslObjectName,tslPropertyDot,tslPropertyName +syn match tslObjectName '\h\w*\ze\.\h\w*' contained +syn match tslPropertyDot '\.' contained +syn match tslPropertyName '\.\@<=\h\w*\%(\ze\.\|\ze\s*[^(]\|\ze\s*$\)' contained + +# Function calls +syn match tslFuncCallName '\%(\<\%(function\|procedure\)\s\+\%(\h\w*\.\)\?\)\@=!&|^~%]' +syn match tslOperator ':=' +syn match tslDelimiter '[()[\]{},;:.@?]' + +# Comments +syn match tslComment '//.*$' contains=tslTodo,@Spell +syn region tslComment start='(\*' end='\*)' contains=tslTodo,@Spell keepend +syn region tslComment start='{' end='}' contains=tslTodo,@Spell keepend + +# Strings +syn region tslString start=+[uU]\=\z(['"]\)+ end='\z1' skip='\\\\\|\\\z1' +syn region tslRawString start=+[uU]\=[rR]\z(['"]\)+ end='\z1' skip='\\\\\|\\\z1' + +# Numbers +syn match tslNumber '\<0[oO]\=\o\+[Ll]\=\>' +syn match tslNumber '\<0[xX]\x\+[Ll]\=\>' +syn match tslNumber '\<0[bB][01]\+[Ll]\=\>' +syn match tslNumber '\<\%([1-9]\d*\|0\)[Ll]\=\>' +syn match tslNumber '\<\d\+[jJ]\>' +syn match tslNumber '\<\d\+[eE][+-]\=\d\+[jJ]\=\>' +syn match tslNumber '\<\d\+\.\%([eE][+-]\=\d\+\)\=[jJ]\=\%(\W\|$\)\@=' +syn match tslNumber '\%(^\|\W\)\zs\d*\.\d\+\%([eE][+-]\=\d\+\)\=[jJ]\=\>' + +# Highlight links +hi def link tslClassName Type +hi def link tslDot Operator +hi def link tslPropertyName Special + +hi def link tslHeader Statement +hi def link tslFuncName Function +hi def link tslTypeNameInFunc Type +hi def link tslParam Identifier +hi def link tslParamType Type +hi def link tslReturnType Type +hi def link tslTypeName Type +hi def link tslParamSep Delimiter +hi def link tslColon Delimiter + +hi def link tslFuncCallName Function +hi def link tslCallParam Identifier +hi def link tslCallValue Type +hi def link tslCallSep Delimiter + +hi def link tslBuiltinVar Constant +hi def link tslConditional Conditional +hi def link tslStatement Statement +hi def link tslInterface Statement +hi def link tslRepeat Repeat +hi def link tslBranch Conditional +hi def link tslReturn Statement +hi def link tslLabel Label +hi def link tslOperator Operator +hi def link tslDelimiter Delimiter +hi def link tslException Exception +hi def link tslBoolean Boolean +hi def link tslNil Constant +hi def link tslBuiltins Constant +hi def link tslShiftOperator Operator +hi def link tslLogicOperator Operator +hi def link tslArithmOperator Operator +hi def link tslSql Keyword +hi def link tslSqlOperator Special +hi def link tslOther Special +hi def link tslScope StorageClass +hi def link tslClass Statement +hi def link tslClassModifier Identifier +hi def link tslConstruct Special +hi def link tslAccess Statement +hi def link tslTodo Todo +hi def link tslComment Comment +hi def link tslString String +hi def link tslRawString String +hi def link tslQuotes String +hi def link tslNumber Number + +b:current_syntax = "tsl" + +&cpo = cpo_save diff --git a/vscode/tsl-syntax/README.md b/vscode/tsl-syntax/README.md new file mode 100644 index 0000000..f4e1c95 --- /dev/null +++ b/vscode/tsl-syntax/README.md @@ -0,0 +1,12 @@ +# TSL Syntax + +`VSCode` 的 `ts[lf]`语法插件 + +## 编译&安装 + +```bash +npm install +vsce package + +code --install-extension tsl-syntax-1.0.0.vsix +``` diff --git a/vscode/tsl-syntax/images/ts.png b/vscode/tsl-syntax/images/ts.png new file mode 100644 index 0000000..bda5035 Binary files /dev/null and b/vscode/tsl-syntax/images/ts.png differ diff --git a/vscode/tsl-syntax/language-configuration.json b/vscode/tsl-syntax/language-configuration.json new file mode 100644 index 0000000..75fa918 --- /dev/null +++ b/vscode/tsl-syntax/language-configuration.json @@ -0,0 +1,36 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": ["(*", "*)"] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "folding": { + "markers": { + "start": "^\\s*\\{\\s*$", + "end": "^\\s*\\}\\s*$" + } + }, + "wordPattern": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b", + "indentationRules": { + "increaseIndentPattern": "^.*(\\{[^}\"']*|\\([^)\"']*|\\[[^\\]\"']*)$", + "decreaseIndentPattern": "^(.*\\*\\/)?\\s*[\\}\\]\\)].*$" + } +} diff --git a/vscode/tsl-syntax/package-lock.json b/vscode/tsl-syntax/package-lock.json new file mode 100644 index 0000000..3d6f1de --- /dev/null +++ b/vscode/tsl-syntax/package-lock.json @@ -0,0 +1,40 @@ +{ + "name": "tsl-syntax", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tsl-syntax", + "version": "1.0.0", + "devDependencies": { + "@types/vscode": "^1.101.0", + "typescript": "^5.8.3" + }, + "engines": { + "vscode": "^1.101.0" + } + }, + "node_modules/@types/vscode": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.101.0.tgz", + "integrity": "sha512-ZWf0IWa+NGegdW3iU42AcDTFHWW7fApLdkdnBqwYEtHVIBGbTu0ZNQKP/kX3Ds/uMJXIMQNAojHR4vexCEEz5Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/vscode/tsl-syntax/package.json b/vscode/tsl-syntax/package.json new file mode 100644 index 0000000..7a9e7db --- /dev/null +++ b/vscode/tsl-syntax/package.json @@ -0,0 +1,51 @@ +{ + "name": "tsl-syntax", + "displayName": "TSL Syntax", + "description": "Syntax highlighting for tsl/tsf files", + "version": "1.0.0", + "publisher": "csh", + "homepage": "https://git.mytsl.cn/csh/tsl-syntax", + "repository": { + "type": "git", + "url": "https://git.mytsl.cn/csh/tsl-syntax" + }, + "bugs": { + "url": "https://git.mytsl.cn/csh/tsl-syntax/issues" + }, + "icon": "images/ts.png", + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "tsl", + "aliases": [ + "TSL", + "tsl" + ], + "extensions": [ + ".tsl", + ".tsf" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "tsl", + "scopeName": "source.tsl", + "path": "./syntaxes/tsl.tmLanguage.json" + } + ] + }, + "scripts": {}, + "devDependencies": { + "@types/vscode": "^1.101.0", + "@vscode/vsce": "^3.5.0", + "typescript": "^5.8.3" + }, + "engines": { + "vscode": "^1.101.0" + } +} \ No newline at end of file diff --git a/vscode/tsl-syntax/syntaxes/tsl.tmLanguage.json b/vscode/tsl-syntax/syntaxes/tsl.tmLanguage.json new file mode 100644 index 0000000..70c260d --- /dev/null +++ b/vscode/tsl-syntax/syntaxes/tsl.tmLanguage.json @@ -0,0 +1,438 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "TSL", + "scopeName": "source.tsl", + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#strings" + }, + { + "include": "#numbers" + }, + { + "include": "#keywords" + }, + { + "include": "#functions" + }, + { + "include": "#function-calls" + }, + { + "include": "#property-chains" + }, + { + "include": "#operators" + }, + { + "include": "#types" + }, + { + "include": "#builtins" + }, + { + "include": "#identifiers" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.line.double-slash.tsl", + "match": "//.*$", + "captures": { + "0": { + "patterns": [ + { + "include": "#comment-todo" + } + ] + } + } + }, + { + "name": "comment.block.pascal.tsl", + "begin": "\\(\\*", + "end": "\\*\\)", + "patterns": [ + { + "include": "#comment-todo" + } + ] + }, + { + "name": "comment.block.curly.tsl", + "begin": "\\{", + "end": "\\}", + "patterns": [ + { + "include": "#comment-todo" + } + ] + } + ] + }, + "comment-todo": { + "patterns": [ + { + "name": "keyword.other.todo.tsl", + "match": "\\b(FIXME|NOTE|NOTES|TODO|XXX)\\b" + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double.tsl", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.tsl", + "match": "\\\\." + } + ] + }, + { + "name": "string.quoted.single.tsl", + "begin": "'", + "end": "'", + "patterns": [ + { + "name": "constant.character.escape.tsl", + "match": "\\\\." + } + ] + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric.hex.tsl", + "match": "\\b0[xX][0-9a-fA-F]+[Ll]?\\b" + }, + { + "name": "constant.numeric.octal.tsl", + "match": "\\b0[oO]?[0-7]+[Ll]?\\b" + }, + { + "name": "constant.numeric.binary.tsl", + "match": "\\b0[bB][01]+[Ll]?\\b" + }, + { + "name": "constant.numeric.float.tsl", + "match": "\\b\\d+\\.\\d*([eE][+-]?\\d+)?[jJ]?\\b" + }, + { + "name": "constant.numeric.float.tsl", + "match": "\\b\\d*\\.\\d+([eE][+-]?\\d+)?[jJ]?\\b" + }, + { + "name": "constant.numeric.scientific.tsl", + "match": "\\b\\d+[eE][+-]?\\d+[jJ]?\\b" + }, + { + "name": "constant.numeric.integer.tsl", + "match": "\\b([1-9]\\d*|0)[Ll]?\\b" + }, + { + "name": "constant.numeric.complex.tsl", + "match": "\\b\\d+[jJ]\\b" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "keyword.control.conditional.tsl", + "match": "\\b(if|else|case|of)\\b" + }, + { + "name": "keyword.control.repeat.tsl", + "match": "\\b(for|while|do|downto|step|until|repeat|to)\\b" + }, + { + "name": "keyword.control.flow.tsl", + "match": "\\b(break|continue|exit|return|debugreturn|debugrunenv|debugrunenvdo)\\b" + }, + { + "name": "keyword.control.exception.tsl", + "match": "\\b(try|except|finally|raise|exceptobject)\\b" + }, + { + "name": "keyword.other.unit.tsl", + "match": "\\b(unit|uses|implementation|interface|initalization|finalization)\\b" + }, + { + "name": "storage.type.class.tsl", + "match": "\\b(class|type|fakeclass|new)\\b" + }, + { + "name": "storage.modifier.access.tsl", + "match": "\\b(public|protected|private|published)\\b" + }, + { + "name": "storage.modifier.tsl", + "match": "\\b(global|static|external|const|out|var)\\b" + }, + { + "name": "keyword.other.tsl", + "match": "\\b(begin|end|then|this|with|weakref|autoref|namespace|goto|label)\\b" + }, + { + "name": "keyword.other.calling-convention.tsl", + "match": "\\b(cdecl|pascal|stdcall|safecall|fastcall|register)\\b" + }, + { + "name": "keyword.other.class-modifier.tsl", + "match": "\\b(override|overload|virtual|property|self|inherited)\\b" + }, + { + "name": "keyword.other.construct.tsl", + "match": "\\b(create|destroy|operator)\\b" + }, + { + "name": "keyword.other.special.tsl", + "match": "\\b(setuid|sudo)\\b" + } + ] + }, + "functions": { + "patterns": [ + { + "name": "meta.function.tsl", + "begin": "\\b(program|function|procedure)\\s+", + "beginCaptures": { + "1": { + "name": "storage.type.function.tsl" + } + }, + "end": "(?=;|\\n|\\{|begin)", + "patterns": [ + { + "name": "entity.name.function.tsl", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)*\\b" + }, + { + "include": "#function-parameters" + } + ] + } + ] + }, + "function-parameters": { + "patterns": [ + { + "name": "meta.function.parameters.tsl", + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "name": "variable.parameter.tsl", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b" + }, + { + "name": "punctuation.separator.parameter.tsl", + "match": "[;,]" + }, + { + "name": "punctuation.separator.type.tsl", + "match": ":" + }, + { + "include": "#types" + } + ] + } + ] + }, + "function-calls": { + "patterns": [ + { + "name": "meta.function-call.method.tsl", + "begin": "\\b([a-zA-Z_][a-zA-Z0-9_]*)(\\.)([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(", + "beginCaptures": { + "1": { + "name": "variable.other.object.tsl" + }, + "2": { + "name": "punctuation.accessor.tsl" + }, + "3": { + "name": "entity.name.function.call.method.tsl" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#function-call-parameters" + } + ] + }, + { + "name": "meta.function-call.tsl", + "begin": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(", + "beginCaptures": { + "1": { + "name": "entity.name.function.call.tsl" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#function-call-parameters" + } + ] + } + ] + }, + "function-call-parameters": { + "patterns": [ + { + "name": "variable.parameter.named.tsl", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*(?=\\s*:)" + }, + { + "name": "punctuation.separator.parameter.tsl", + "match": "[;,]" + }, + { + "name": "punctuation.separator.type.tsl", + "match": ":" + }, + { + "include": "#strings" + }, + { + "include": "#numbers" + }, + { + "include": "#identifiers" + } + ] + }, + "property-chains": { + "patterns": [ + { + "name": "meta.property-access.chained.tsl", + "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)((?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+)(?!\\s*\\()", + "captures": { + "1": { + "name": "variable.other.object.tsl" + }, + "2": { + "patterns": [ + { + "match": "(\\.)([a-zA-Z_][a-zA-Z0-9_]*)", + "captures": { + "1": { + "name": "punctuation.accessor.tsl" + }, + "2": { + "name": "variable.other.property.tsl" + } + } + } + ] + } + } + }, + { + "name": "meta.property-access.simple.tsl", + "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)(\\.)([a-zA-Z_][a-zA-Z0-9_]*)(?!\\s*\\()", + "captures": { + "1": { + "name": "variable.other.object.tsl" + }, + "2": { + "name": "punctuation.accessor.tsl" + }, + "3": { + "name": "variable.other.property.tsl" + } + } + } + ] + }, + "operators": { + "patterns": [ + { + "name": "keyword.operator.assignment.tsl", + "match": ":=" + }, + { + "name": "keyword.operator.arithmetic.tsl", + "match": "[+\\-*/<>=]|\\b(div|mod)\\b" + }, + { + "name": "keyword.operator.logical.tsl", + "match": "\\b(and|or|not|in|is)\\b" + }, + { + "name": "keyword.operator.bitwise.tsl", + "match": "\\b(ror|rol|shr|shl)\\b" + }, + { + "name": "keyword.operator.io.tsl", + "match": "\\b(write|read)\\b" + }, + { + "name": "keyword.operator.set.tsl", + "match": "\\b(union|minus|union2)\\b" + }, + { + "name": "keyword.operator.other.tsl", + "match": "[!&|^~%]" + }, + { + "name": "punctuation.separator.tsl", + "match": "[()\\[\\]{},;:.@?]" + } + ] + }, + "types": { + "patterns": [ + { + "name": "storage.type.primitive.tsl", + "match": "\\b(string|integer|boolean|int64|real)\\b" + } + ] + }, + "builtins": { + "patterns": [ + { + "name": "constant.language.boolean.tsl", + "match": "\\b(true|false)\\b" + }, + { + "name": "constant.language.null.tsl", + "match": "\\bnil\\b" + }, + { + "name": "support.variable.builtin.tsl", + "match": "\\b(paramcount|realparamcount|params|system|thisfunction|tslassigning|likeeps|likeepsrate)\\b" + }, + { + "name": "support.function.builtin.tsl", + "match": "\\b(echo|mtic|mtoc|this|inf|nan)\\b" + }, + { + "name": "keyword.other.sql.tsl", + "match": "\\b(select|vselect|sselect|update|delete|mselect|set|sqlin|from|where|group|by|like|order)\\b" + } + ] + }, + "identifiers": { + "patterns": [ + { + "name": "variable.other.tsl", + "match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b" + } + ] + } + } +} \ No newline at end of file diff --git a/vscode/tsl-syntax/tsl-syntax-1.0.0.vsix b/vscode/tsl-syntax/tsl-syntax-1.0.0.vsix new file mode 100644 index 0000000..da045dc Binary files /dev/null and b/vscode/tsl-syntax/tsl-syntax-1.0.0.vsix differ