From 86a8ea57bb3cb651cf9bee3fd1ae37f7e134c526 Mon Sep 17 00:00:00 2001
From: ath3 <45574139+ath3@users.noreply.github.com>
Date: Fri, 26 Aug 2022 00:59:02 +0200
Subject: [PATCH] Pascal support (#3542)

---
 book/src/generated/lang-support.md     |   1 +
 languages.toml                         |  14 +
 runtime/queries/pascal/highlights.scm  | 382 +++++++++++++++++++++++++
 runtime/queries/pascal/injections.scm  |   2 +
 runtime/queries/pascal/textobjects.scm |  10 +
 5 files changed, 409 insertions(+)
 create mode 100644 runtime/queries/pascal/highlights.scm
 create mode 100644 runtime/queries/pascal/injections.scm
 create mode 100644 runtime/queries/pascal/textobjects.scm

diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md
index 8c1d8208..8d8275ab 100644
--- a/book/src/generated/lang-support.md
+++ b/book/src/generated/lang-support.md
@@ -76,6 +76,7 @@
 | odin | ✓ |  |  | `ols` |
 | openscad | ✓ |  |  | `openscad-language-server` |
 | org | ✓ |  |  |  |
+| pascal | ✓ | ✓ |  | `pasls` |
 | perl | ✓ | ✓ | ✓ |  |
 | php | ✓ | ✓ | ✓ | `intelephense` |
 | prisma | ✓ |  |  | `prisma-language-server` |
diff --git a/languages.toml b/languages.toml
index 0bcc6ef2..986b927e 100644
--- a/languages.toml
+++ b/languages.toml
@@ -1691,3 +1691,17 @@ roots = ["edgedb.toml"]
 [[grammar]]
 name ="esdl"
 source = { git = "https://github.com/greym0uth/tree-sitter-esdl", rev = "b840c8a8028127e0a7c6e6c45141adade2bd75cf" }
+
+[[language]]
+name = "pascal"
+scope = "source.pascal"
+injection-regex = "pascal"
+file-types = ["pas", "pp", "inc", "lpr", "lfm"]
+roots = []
+comment-token = "//"
+indent = { tab-width = 2, unit = "  " }
+language-server = { command = "pasls", args = [] }
+
+[[grammar]]
+name = "pascal"
+source = { git = "https://github.com/Isopod/tree-sitter-pascal", rev = "2fd40f477d3e2794af152618ccfac8d92eb72a66" }
diff --git a/runtime/queries/pascal/highlights.scm b/runtime/queries/pascal/highlights.scm
new file mode 100644
index 00000000..5368a22b
--- /dev/null
+++ b/runtime/queries/pascal/highlights.scm
@@ -0,0 +1,382 @@
+; -- Identifier type inferrence
+
+; VERY QUESTIONABLE: Highlighting of identifiers based on spelling
+(exprBinary ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(exprUnary ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(assignment rhs: ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(exprBrackets ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(exprParens ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(exprDot rhs: ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(exprTpl args: ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(exprArgs ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(declEnumValue ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+(defaultValue ((identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]+$|^[a-z]{2,3}[A-Z].+$")))
+
+; -- Break, Continue & Exit
+; (Not ideal: ideally, there would be a way to check if these special
+; identifiers are shadowed by a local variable)
+(statement ((identifier) @keyword.control.return
+ (#match? @keyword.control.return "^[eE][xX][iI][tT]$")))
+(statement (exprCall entity: ((identifier) @keyword.control.return
+ (#match? @keyword.control.return "^[eE][xX][iI][tT]$"))))
+(statement ((identifier) @keyword.control.repeat
+ (#match? @keyword.control.repeat "^[bB][rR][eE][aA][kK]$")))
+(statement ((identifier) @keyword.control.repeat
+ (#match? @keyword.control.repeat "^[cC][oO][nN][tT][iI][nN][uU][eE]$")))
+
+; -- Heuristic for procedure/function calls without parentheses
+; (If a statement consists only of an identifier, assume it's a procedure)
+; (This will still not match all procedure calls, and also may produce false
+; positives in rare cases, but only for nonsensical code)
+
+(statement (exprDot rhs: (exprTpl entity: (identifier) @function)))
+(statement (exprTpl entity: (identifier) @function))
+(statement (exprDot rhs: (identifier) @function))
+(statement (identifier) @function)
+
+; -- Procedure name in calls with parentheses
+; (Pascal doesn't require parentheses for procedure calls, so this will not
+; detect all calls)
+
+(inherited) @function
+
+; foo.bar<t>
+(exprCall entity: (exprDot rhs: (exprTpl entity: (identifier) @function)))
+; foo.bar
+(exprCall entity: (exprDot rhs: (identifier) @function))
+; foobar<t>
+(exprCall entity: (exprTpl entity: (identifier) @function))
+; foobar
+(exprCall entity: (identifier) @function)
+
+; -- Fields
+
+(declSection (declVars (declVar   name:(identifier) @variable.other.member)))
+(declSection (declField name:(identifier) @variable.other.member))
+(declClass   (declField name:(identifier) @variable.other.member))
+(exprDot rhs: (exprDot)    @variable.other.member)
+(exprDot rhs: (identifier) @variable.other.member)
+
+(recInitializerField name:(identifier) @variable.other.member)
+
+; -- Variable & constant declarations
+; (This is only questionable because we cannot detect types of identifiers
+; declared in other units, so the results will be inconsistent)
+
+(declVar name: (identifier) @variable)
+(declConst name: (identifier) @constant)
+(declEnumValue name: (identifier) @constant)
+
+; -- Constant usage
+
+[
+	(caseLabel)
+	(label)
+] @constant
+
+(procAttribute (identifier) @constant)
+(procExternal (identifier) @constant)
+
+; -- Type usage
+
+(typeref) @type
+
+; -- Exception parameters
+
+(exceptionHandler variable: (identifier) @variable.parameter)
+
+; -- Template parameters
+
+(genericArg	type: (typeref) @type)
+(genericArg	name: (identifier) @variable.parameter)
+
+(declProc name: (genericDot lhs: (identifier) @type))
+(declType (genericDot (identifier) @type))
+
+(genericDot (genericTpl (identifier) @type))
+(genericDot (genericDot (identifier) @type))
+
+(genericTpl entity: (genericDot (identifier) @type))
+(genericTpl entity: (identifier) @type)
+
+; -- Function parameters
+
+(declArg name: (identifier) @variable.parameter)
+
+; Treat property declarations like functions
+
+(declProp name: (identifier) @function)
+(declProp getter: (identifier) @variable.other.member)
+(declProp setter: (identifier) @variable.other.member)
+
+; -- Procedure & function declarations
+
+; foo.bar<t>
+(declProc name: (genericDot rhs: (genericTpl entity: (identifier) @function)))
+; foo.bar
+(declProc name: (genericDot rhs: (identifier) @function))
+; foobar<t>
+(declProc name: (genericTpl entity: (identifier) @function))
+; foobar
+(declProc name: (identifier) @function)
+
+; -- Type declaration
+
+(declType name: (genericTpl entity: (identifier) @type))
+(declType name: (identifier) @type)
+
+; -- Comments
+
+(comment)         @comment
+(pp)              @function.macro
+
+; -- Variables
+
+(exprBinary (identifier) @variable)
+(exprUnary (identifier) @variable)
+(assignment (identifier) @variable)
+(exprBrackets (identifier) @variable)
+(exprParens (identifier) @variable)
+(exprDot (identifier) @variable)
+(exprTpl (identifier) @variable)
+(exprArgs (identifier) @variable)
+(defaultValue (identifier) @variable)
+
+; -- Literals
+
+(literalNumber)   @constant.builtin.numeric
+(literalString)   @string
+
+; -- Builtin constants
+
+[
+	(kTrue)
+	(kFalse)
+] @constant.builtin.boolean
+
+[
+	(kNil)
+] @constant.builtin
+
+; -- Punctuation & operators
+
+[
+	(kOr)
+	(kXor)
+	(kDiv)
+	(kMod)
+	(kAnd)
+	(kShl)
+	(kShr)
+	(kNot)
+	(kIs)
+	(kAs)
+	(kIn)
+] @keyword.operator
+
+[
+	(kDot)
+	(kAdd)
+	(kSub)
+	(kMul)
+	(kFdiv)
+	(kAssign)
+	(kAssignAdd)
+	(kAssignSub)
+	(kAssignMul)
+	(kAssignDiv)
+	(kEq)
+	(kLt)
+	(kLte)
+	(kGt)
+	(kGte)
+	(kNeq)
+	(kAt)
+	(kHat)
+] @operator
+
+[
+	".."
+] @punctuation.special
+
+[
+	";"
+	","
+	":"
+	(kEndDot)
+] @punctuation.delimiter
+
+[
+	"("
+	")"
+	"["
+	"]"
+] @punctuation.bracket
+
+; -- Attributes
+
+(procAttribute (kPublic) @attribute)
+
+[
+	(kDefault)
+	(kIndex)
+	(kNodefault)
+	(kStored)
+
+	(kStatic)
+	(kVirtual)
+	(kAbstract)
+	(kSealed)
+	(kDynamic)
+	(kOverride)
+	(kOverload)
+	(kReintroduce)
+	(kInline)
+
+	(kForward)
+
+	(kStdcall)
+	(kCdecl)
+	(kCppdecl)
+	(kPascal)
+	(kRegister)
+	(kMwpascal)
+	(kExternal)
+	(kName)
+	(kMessage)
+	(kDeprecated)
+	(kExperimental)
+	(kPlatform)
+	(kUnimplemented)
+	(kCvar)
+	(kExport)
+	(kFar)
+	(kNear)
+	(kSafecall)
+	(kAssembler)
+	(kNostackframe)
+	(kInterrupt)
+	(kNoreturn)
+	(kIocheck)
+	(kLocal)
+	(kHardfloat)
+	(kSoftfloat)
+	(kMs_abi_default)
+	(kMs_abi_cdecl)
+	(kSaveregisters)
+	(kSysv_abi_default)
+	(kSysv_abi_cdecl)
+	(kVectorcall)
+	(kVarargs)
+	(kWinapi)
+	(kAlias)
+	(kDelayed)
+
+	(rttiAttributes)
+	(procAttribute)
+
+] @attribute
+
+; -- Keywords
+[
+	(kProgram)
+	(kLibrary)
+	(kUnit)
+	(kUses)
+
+	(kBegin)
+	(kEnd)
+	(kAsm)
+
+	(kVar)
+	(kThreadvar)
+	(kConst)
+	(kResourcestring)
+	(kConstref)
+	(kOut)
+	(kType)
+	(kLabel)
+	(kExports)
+
+	(kAbsolute)
+
+	(kProperty)
+	(kRead)
+	(kWrite)
+	(kImplements)
+
+	(kClass)
+	(kInterface)
+	(kObject)
+	(kRecord)
+	(kObjcclass)
+	(kObjccategory)
+	(kObjcprotocol)
+	(kArray)
+	(kFile)
+	(kString)
+	(kSet)
+	(kOf)
+	(kHelper)
+	(kPacked)
+
+	(kInherited)
+
+	(kGeneric)
+	(kSpecialize)
+
+	(kFunction)
+	(kProcedure)
+	(kConstructor)
+	(kDestructor)
+	(kOperator)
+	(kReference)
+
+	(kInterface)
+	(kImplementation)
+	(kInitialization)
+	(kFinalization)
+
+	(kPublished)
+	(kPublic)
+	(kProtected)
+	(kPrivate)
+	(kStrict)
+	(kRequired)
+	(kOptional)
+
+	(kTry)
+	(kExcept)
+	(kFinally)
+	(kRaise)
+	(kOn)
+	(kCase)
+	(kWith)
+	(kGoto)
+] @keyword
+
+[
+	(kFor)
+	(kTo)
+	(kDownto)
+	(kDo)
+	(kWhile)
+	(kRepeat)
+	(kUntil)
+] @keyword.control.repeat
+
+[
+	(kIf)
+	(kThen)
+	(kElse)
+] @keyword.control.conditional
diff --git a/runtime/queries/pascal/injections.scm b/runtime/queries/pascal/injections.scm
new file mode 100644
index 00000000..321c90ad
--- /dev/null
+++ b/runtime/queries/pascal/injections.scm
@@ -0,0 +1,2 @@
+((comment) @injection.content
+ (#set! injection.language "comment"))
diff --git a/runtime/queries/pascal/textobjects.scm b/runtime/queries/pascal/textobjects.scm
new file mode 100644
index 00000000..56dbe540
--- /dev/null
+++ b/runtime/queries/pascal/textobjects.scm
@@ -0,0 +1,10 @@
+
+(declType (declClass (declSection) @class.inside)) @class.around
+
+(defProc body: (_) @function.inside) @function.around
+
+(declArgs (_) @parameter.inside) @parameter.around
+(exprArgs (_) @parameter.inside) @parameter.around
+
+(comment) @comment.inside
+(comment)+ @comment.around