Skip to content

Commit

Permalink
feat(code): add syntax highlighting for Crystal (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
xiBread authored Jul 28, 2021
1 parent b21dd83 commit ecd15cc
Show file tree
Hide file tree
Showing 4 changed files with 388 additions and 0 deletions.
33 changes: 33 additions & 0 deletions app/src/main/java/com/discord/simpleast/sample/SampleTexts.kt
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,38 @@ object SampleTexts {
```
"""

private const val CODE_BLOCK_CRYSTAL = """
Crystal code block:
```cr
regex = /\bs|d\b/i
match = regex.match("start here but end here")
# New Class
@[Species(type: "human")]
class Person(T)
property balance
def initialize(@name : String)
@age = 0
@balance = 100
end
def grow(years = 1)
@age += years
end
def info
puts "My name is #{@name} and I am #{@age}"
end
def buy(item : T)
@balance -= 5
end
end
```
"""

private const val CODE_BLOCK_JAVASCRIPT = """
JavaScript code block:
```js
Expand Down Expand Up @@ -232,6 +264,7 @@ object SampleTexts {
$CODE_BLOCK_RUST
$CODE_BLOCK_SQL
$CODE_BLOCK_XML
$CODE_BLOCK_CRYSTAL
$CODE_BLOCK_JAVASCRIPT
That should do it....
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ object CodeRules {
PATTERN_TEXT.toMatchGroupRule(),
)

val crystalRules = createGenericCodeRules<R, S>(
codeStyleProviders,
additionalRules = Crystal.createCrystalCodeRules(codeStyleProviders),
definitions = arrayOf("def", "class"),
builtIns = Crystal.BUILT_INS,
keywords = Crystal.KEYWORDS)

val javascriptRules = createGenericCodeRules<R, S>(
codeStyleProviders,
additionalRules = JavaScript.createCodeRules(codeStyleProviders),
Expand Down Expand Up @@ -216,6 +223,9 @@ object CodeRules {
"xml" to xmlRules,
"http" to xmlRules,

"cr" to crystalRules,
"crystal" to crystalRules,

"js" to javascriptRules,
"javascript" to javascriptRules,
)
Expand Down
135 changes: 135 additions & 0 deletions simpleast-core/src/main/java/com/discord/simpleast/code/Crystal.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.discord.simpleast.code

import com.discord.simpleast.code.CodeRules.toMatchGroupRule
import com.discord.simpleast.core.node.Node
import com.discord.simpleast.core.node.StyleNode
import com.discord.simpleast.core.parser.ParseSpec
import com.discord.simpleast.core.parser.Parser
import com.discord.simpleast.core.parser.Rule
import java.util.regex.Matcher
import java.util.regex.Pattern

object Crystal {

val KEYWORDS: Array<String> = arrayOf(
"true|false|nil",
"module|require|include|extend|lib",
"abstract|private|protected",
"annotation|class|finalize|new|initialize|allocate|self|super",
"union|typeof|forall|is_a?|nil?|as?|as|responds_to?|alias|type",
"property|getter|setter|struct|of",
"previous_def|method|fun|enum|macro",
"rescue|raise|begin|end|ensure",
"if|else|elsif|then|unless|until",
"for|in|of|do|when|select|with",
"while|break|next|yield|case",
"print|puts|return",
)

val BUILT_INS = arrayOf(
"Nil|Bool|true|false|Void|NoReturn",
"Number|BigDecimal|BigRational|BigFloat|BigInt",
"Int|Int8|Int16|Int32|Int64|UInt8|UInt16|UInt32|UInt64|Float|Float32|Float64",
"Char|String|Symbol|Regex",
"StaticArray|Array|Set|Hash|Range|Tuple|NamedTuple|Union|BitArray",
"Proc|Command|Enum|Class",
"Reference|Value|Struct|Object|Pointer",
"Exception|ArgumentError|KeyError|TypeCastError|IndexError|RuntimeError|NilAssertionError|InvalidBigDecimalException|NotImplementedError|OverflowError",
"pointerof|sizeof|instance_sizeof|offsetof|uninitialized"
)

class FunctionNode<RC>(
pre: String, signature: String, params: String?,
codeStyleProviders: CodeStyleProviders<RC>
): Node.Parent<RC>(
StyleNode.TextStyledNode(pre, codeStyleProviders.keywordStyleProvider),
StyleNode.TextStyledNode(signature, codeStyleProviders.identifierStyleProvider),
params?.let { StyleNode.TextStyledNode(it, codeStyleProviders.paramsStyleProvider) },
) {
companion object {

/**
* Matches against a crystal function declaration
*
* ```
* def initialize(val : T)
* def increment(amount)
* private def log
* ```
*/
private val PATTERN_CRYSTAL_FUNC =
Pattern.compile("""^(def)( +\w+)( *\( *(?:@\w+ +: +\w*)?\w+(?: +[:=] +.*)? *\))?(?!.+)""")

fun <RC, S> createFunctionRule(codeStyleProviders: CodeStyleProviders<RC>) =
object : Rule<RC, Node<RC>, S>(PATTERN_CRYSTAL_FUNC) {
override fun parse(matcher: Matcher, parser: Parser<RC, in Node<RC>, S>, state: S): ParseSpec<RC, S> {
val definition = matcher.group(1)
val signature = matcher.group(2)
val params = matcher.group(3)
return ParseSpec.createTerminal(FunctionNode(definition!!, signature!!, params, codeStyleProviders), state)
}
}
}
}

private val PATTERN_CRYSTAL_COMMENTS =
Pattern.compile("""^(#.*)""")

/**
* Matches against crystal annotations
*
* ```
* @[Annotation(key: "value")]
* @[Annotation(1, 2, 3)]
* @[Annotation]
* ```
*/
private val PATTERN_CRYSTAL_ANNOTATION =
Pattern.compile("""^@\[(\w+)(?:\(.+\))?]""")

/**
* Matches against a crystal string or character
*
* ```
* "hello"
* "hello
* world"
* 'a'
* ```
*/
private val PATTERN_CRYSTAL_STRINGS =
Pattern.compile("""^"[\s\S]*?(?<!\\)"(?=\W|\s|$)""")

/**
* Matches against a crystal regex
*
* ```
* /foo/m
* ```
*/
private val PATTERN_CRYSTAL_REGEX =
Pattern.compile("""^/.*?/[imx]?""")

/**
* Matches against a crystal symbol
*
* ```
* :symbol
* :"quoted"
* :<<
*/
private val PATTERN_CRYSTAL_SYMBOL =
Pattern.compile("""^(:"?(?:[+-/%&^|]|\*\*?|\w+|(?:<(?=[<=\s])[<=]?(?:(?<==)>)?|>(?=[>=\s])[>=]?(?:(?<==)>)?)|\[][?=]?|(?:!(?=[=~\s])[=~]?|=?(?:~|==?)))(?:(?<!\\)"(?=\s|$))?)""")

internal fun <RC, S> createCrystalCodeRules(
codeStyleProviders: CodeStyleProviders<RC>
): List<Rule<RC, Node<RC>, S>> =
listOf(
PATTERN_CRYSTAL_COMMENTS.toMatchGroupRule(stylesProvider = codeStyleProviders.commentStyleProvider),
PATTERN_CRYSTAL_STRINGS.toMatchGroupRule(stylesProvider = codeStyleProviders.literalStyleProvider),
PATTERN_CRYSTAL_REGEX.toMatchGroupRule(stylesProvider = codeStyleProviders.literalStyleProvider),
PATTERN_CRYSTAL_ANNOTATION.toMatchGroupRule(stylesProvider = codeStyleProviders.genericsStyleProvider),
PATTERN_CRYSTAL_SYMBOL.toMatchGroupRule(stylesProvider = codeStyleProviders.literalStyleProvider),
FunctionNode.createFunctionRule(codeStyleProviders),
)
}
Loading

0 comments on commit ecd15cc

Please sign in to comment.