Skip to content

Instantly share code, notes, and snippets.

@UltraInstinct05
Last active April 11, 2021 13:21
Show Gist options
  • Save UltraInstinct05/96fa99e1aaeb32b12d1e62109d61fcc2 to your computer and use it in GitHub Desktop.
Save UltraInstinct05/96fa99e1aaeb32b12d1e62109d61fcc2 to your computer and use it in GitHub Desktop.
A syntax for Cottle.
%YAML 1.2
---
file_extensions:
- cottle
scope: source.cottle
contexts:
main:
- include: cottle_code_block
- include: expressions
comment:
- match: \_(?!\w+)
scope: punctuation.definition.comment.cottle
push: comment_body
comment_body:
- meta_scope: comment.line.underscore.cottle
- match: ($\n?)|(?=\})
pop: true
cottle_code_block:
- match: \{
scope: punctuation.section.block.begin.cottle
push: cottle_code_block_body
cottle_code_block_body:
- meta_scope: meta.block.cottle
- match: \}
scope: punctuation.section.block.end.cottle
pop: true
- include: statements
- include: builtin_functions
- include: comment
- include: cottle_code_block
- include: group_expression
- include: expressions
- match: \b(echo|dump|set|to)\b
scope: constant.language.cottle
builtin_functions:
- match: |-
(?x:
\b(and|cmp|default|defined|eq|ge|gt|has|le|lt|ne|not|or|xor|when|abs|add
|ceil|cos|div|floor|max|min|mod|mul|pow|rand|round|sin|sub|cat|cross|
|except|find|filter|flip|join|len|map|range|slice|sort|union|zip|char
|format|lcase|match|ord|split|token|ucase|cast|type|call)\b
)(?=\()
scope: meta.function-call.cottle
captures:
1: support.function.builtin.cottle
2: punctuation.section.arguments.begin.cottle
push: builtin_functions_body
builtin_functions_body:
- match: \(
scope: punctuation.section.arguments.begin.cottle
set: builtin_functions_parameters
builtin_functions_parameters:
- meta_scope: meta.function-call.cottle
- match: \)
scope: punctuation.section.arguments.end.cottle
pop: true
- include: expressions
- match: ','
scope: punctuation.separator.arguments.cottle
statements:
- include: for_statement
- include: while_statement
- include: if_statement
- include: else_statement
- include: elif_statement
while_statement:
- match: \b(while)\b
scope: keyword.control.loop.while.cottle
push: while_statement_body
while_statement_body:
- meta_scope: meta.while.cottle
- match: ':'
scope: punctuation.section.block.loop.while.cottle
pop: true
- include: expressions
for_statement:
- match: \b(for)\b
scope: keyword.control.loop.for.cottle
push: for_statement_body
for_statement_body:
- meta_scope: meta.for.cottle
- match: \b(in)\b
scope: keyword.control.loop.for.in.cottle
set: in_statement
- include: expressions
in_statement:
- match: ':'
scope: punctuation.section.block.loop.for.cottle
pop: true
- include: expressions
else_statement:
- match: (\|\s*)\b(else)\b(?:\s*(:))?
scope: meta.else.cottle
captures:
1: punctuation.definition.else.cottle
2: keyword.control.conditional.else.cottle
3: punctuation.section.conditional.block.else.cottle
if_statement:
- match: \b(if)\b
scope: keyword.control.conditional.if.cottle
push: if_statement_body
if_statement_body:
- meta_scope: meta.if.cottle
- match: ':'
scope: punctuation.section.conditional.block.if.cottle
pop: true
- include: expressions
elif_statement:
- match: (\|\s*)\b(elif)\b
captures:
1: punctuation.definition.elif.cottle
2: keyword.control.conditional.elseif.cottle
push: elif_statement_body
elif_statement_body:
- meta_scope: meta.elif.cottle
- match: ':'
scope: punctuation.section.conditional.block.if.cottle
pop: true
- include: expressions
expressions:
- include: group_expression
- include: map
- include: operators
- include: number
- include: string
- include: booleans
booleans:
- match: \b(true|false)\b
scope: constant.language.boolean.cottle
map:
- match: \[
scope: punctuation.definition.map.begin.cottle
push: map_body
map_body:
- meta_scope: meta.map.cottle
- match: \]
scope: punctuation.definition.map.end.cottle
pop: true
- include: expressions
- match: ':'
scope: punctuation.separator.mapping.key-value.cottle
- match: ','
scope: punctuation.separator.sequence.cottle
group_expression:
- match: \(
scope: punctuation.section.group.begin.cottle
push: group_expression_body
group_expression_body:
- meta_scope: meta.group.cottle
- match: \)
scope: punctuation.section.group.end.cottle
pop: true
- include: expressions
operators:
- match: '(\+|\-|\*|\/|\%)'
scope: keyword.operator.arithmetic.cottle
- match: '[!<>]='
scope: keyword.operator.comparison.cottle
- match: '[<>]'
scope: keyword.operator.comparison.cottle
- match: '[|&]{2}'
scope: keyword.operator.logical.cottle
- match: '='
scope: keyword.operator.assignment.cottle
- match: '!'
scope: keyword.operator.logical.cottle
number:
- match: \d+
scope: meta.number.integer.decimal.cottle
captures:
0: constant.numeric.value.cottle
string:
- include: double_quoted_string
- include: single_quoted_string
double_quoted_string:
- match: \"
scope: punctuation.definition.string.begin.cottle
push: double_quoted_string_body
double_quoted_string_body:
- meta_scope: meta.string.cottle string.quoted.double.cottle
- match: \"
scope: punctuation.definition.string.end.cottle
pop: true
- match: \{
scope: punctuation.section.block.begin.cottle
push: cottle_code_block_body_within_string
cottle_code_block_body_within_string:
- clear_scopes: 1
- meta_scope: meta.block.cottle
- match: \}
scope: punctuation.section.block.end.cottle
pop: true
- include: statements
- include: builtin_functions
- include: comment
- include: cottle_code_block
- include: group_expression
- include: expressions
- match: \b(echo|dump|set|to)\b
scope: constant.language.cottle
single_quoted_string:
- match: \'
scope: punctuation.definition.string.begin.cottle
push: single_quoted_string_body
single_quoted_string_body:
- meta_scope: meta.string.cottle string.quoted.single.cottle
- match: \'
scope: punctuation.definition.string.end.cottle
pop: true
- match: \{
scope: punctuation.section.block.begin.cottle
push: cottle_code_block_body_within_string
@UltraInstinct05
Copy link
Author

I have updated the syntax to allow cottle code blocks within strings (They seem to act like interpolation, but since they don't have a unique syntax, I couldn't think of a way without duplicating the cottle_code_block context (since we also need to clear the string scopes when a cottle code block is within strings)).

With the default Monokai color scheme, the updated syntax definition would give the following for your code snippet. Is this fine ?

image

@UltraInstinct05
Copy link
Author

I have updated the syntax definition to include support for booleans like true or false as per your code snippet.

@parduz
Copy link

parduz commented Apr 11, 2021

Thanks you so much!!!.

I have updated the syntax to allow cottle code blocks within strings (They seem to act like interpolation, but since they don't have a unique syntax, I couldn't think of a way without duplicating the cottle_code_block context (since we also need to clear the string scopes when a cottle code block is within strings)).

Yup, that's a big problem i was'nt unable to sort out.

With the default Monokai color scheme, the updated syntax definition would give the following for your code snippet. Is this fine ?
image

As you see, the unquoted strings, and all strings outside a code blocks (the very first three one at the top) are still not highlighted as strings.
I tried by addin a "catch .+" at the end to push an "unquoted string" context but it mess everything.
That's a problem i'm unable to solve, while my very main goal is to see all of them, 'cause i want to translate almost 300 scripts: i grouped them all in a single file, and i need to see all the strings (=spoken text) at a glance.

@parduz
Copy link

parduz commented Apr 11, 2021

Question (to learn):
could'nt the first cottle_code_block_body just inlcude the cottle_code_block_body_within_string to avoid duplicated rules?

@UltraInstinct05
Copy link
Author

That is actually very simple. I just thought all the expressions are valid only within those code blocks and hence included them only in that. All that needs to be done is include the expressions context in the main context. With that you'd get highlighting for strings, numbers, maps, groups etc at the top level cottle file & not just within cottle code blocks.

I have updated the syntax definition to do that.

image

@UltraInstinct05
Copy link
Author

UltraInstinct05 commented Apr 11, 2021

could'nt the first cottle_code_block_body just inlcude the cottle_code_block_body_within_string to avoid duplicated rules?

The reason for the duplication is that when a cottle code block is within a string, there is a need to clear the string.quoted... scope so that the highlighting is maintained properly within a string. But if I do that in at the top level cottle_code_block_body, then it would inappropriately clear wanted scopes. So I am not aware of how to workaround this issue without duplicating contexts.

The issue is probably because cottle doesn't have a unique interpolation syntax that allowed to embed cottle code within strings. Otherwise there wasn't a need to do it.

@parduz
Copy link

parduz commented Apr 11, 2021

That is actually very simple. I just thought all the expressions are valid only within those code blocks and hence included them only in that. All that needs to be done is include the expressions context in the main context. With that you'd get highlighting for strings, numbers, maps, groups etc at the top level cottle file & not just within cottle code blocks.

We're almost there, my friend!!!
Here's an updated working script:

{_ this is a comment}

{_ this
 is 
 a 
 comment
}

{set variable to 1}						{_ this is a comment}
string with 1 {variable} inside.		{_ this is a comment}
'string with 1 {variable} inside.'		{_ this is a comment}
"string with 1 {variable} inside."		{_ this is a comment}
{if false:
	string with a {variable} inside.
	'string with a {variable} inside.'
	"string with a {variable} inside."
|else:
	string with a {Emphasize("this is a string_parameter")} inside.
	"string with a {Emphasize("this is a string_parameter")} inside."
	'string with a {Emphasize("this is a {cat("string", "_", "parameter")} ")} inside'.
	'string with a {Emphasize("this is a {						{_ this is a comment}
		{_ this is a comment}
		cat("string", "_", "parameter")
	} ")} inside.'
}

  1. I changed your Comment context in this way:
  comment:
    - match: \{\_(?!\w+)
      scope: punctuation.definition.comment.cottle
      push: comment_body

  comment_body:
    - meta_scope: comment.line.underscore.cottle
    - match: \}
      pop: true

This renders the comments with all the brackets and multiline also
2. I've changed your cottle_code_block_body_within_string context in this way

  cottle_code_block_body_within_string:
    - clear_scopes: 1
    - include: cottle_code_block_body

If i'm not missing something, it works as well without the duplicated code. am i right?

Now, I see i have problems explaining the last part that's not working (i'm italian, pardon me for my poor english) so i changed the color theme to make strings really stand out, with an Orange background. It's ugly but it shows what i need:

image
Look at lines 10, 14, and 18, and also at the last dot at line 20: these should be all strings (so, have the orange background). These are unquoted, but should be catched as strings.
That's the last part not working.

THANKS A LOT AGAIN.

@UltraInstinct05
Copy link
Author

UltraInstinct05 commented Apr 11, 2021

I've changed your cottle_code_block_body_within_string context in this way

Aah ! Good catch with this one. Yeah, this is much more suitable than mine. I don't know why I did not do it this way :P

Look at lines 10, 14, and 18, and also at the last dot at line 20: these should be all strings

Hmm... The issue with this is that they don't have an identifying nature that could be used to mark them as strings ? I thought these are just plain text.

@parduz
Copy link

parduz commented Apr 11, 2021

My understanding is that we should rule that whatever remains that is not already catched by all the syntax rules then is a sring.unquoted. Problem is making it working inside code blocks...?
HOW to make this rule i don't know.

@UltraInstinct05
Copy link
Author

My understanding is that we should rule that whatever remains that is not already catched by all the syntax rules then is a sring.unquoted.

Unfortunately, there isn't (to my knowledge) a way to do something like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment