Skip to content

syntax: add Gleam language support#4045

Open
HiwarkhedePrasad wants to merge 5 commits intomicro-editor:masterfrom
HiwarkhedePrasad:add-gleam-syntax
Open

syntax: add Gleam language support#4045
HiwarkhedePrasad wants to merge 5 commits intomicro-editor:masterfrom
HiwarkhedePrasad:add-gleam-syntax

Conversation

@HiwarkhedePrasad
Copy link

Adds syntax highlighting for the Gleam programming language.

Supports:

  • Keywords (fn, let, case, if, else, import, pub, type, opaque, const, use)
  • Built-in types (Int, Float, String, Bool, List, Option, Result, Nil)
  • Constants (True, False, Nil)
  • Single line comments (//)
  • Strings
  • Numbers

Copy link
Contributor

@Andriamanitra Andriamanitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start but needs some work. I would consider adding highlighting for attributes (@external/@deprecated).

start: "\""
end: "\""
skip: "\\\\."
- special: "\\b[0-9]+\\.?[0-9]*\\b"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Numbers should be constant.number. Gleam also supports a pretty wide range of number literals 1 that this regex doesn't cover. The formats seem to be similar to eg. Python so we can probably just copy them from the Python syntax:

# numbers
- constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal
- constant.number: "\\b0b(_?[01])+\\b" # bin
- constant.number: "\\b0o(_?[0-7])+\\b" # oct
- constant.number: "\\b0x(_?[0-9a-fA-F])+\\b" # hex

Footnotes

  1. https://tour.gleam.run/everything/#basics-number-formats

end: "\""
skip: "\\\\."
- special: "\\b[0-9]+\\.?[0-9]*\\b"
- type: "\\b[A-Z][a-zA-Z0-9_]*\\b" No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overlaps with the definitions for identifier and constant above so those will get colored as type. To fix it you can put this rule before them (later rules are applied on top of earlier ones). It would also make sense to me to change this rule to be identifier, and use type for the built-in types (Int, Float, etc.) that are currently identifier.


rules:
- statement: "\\b(fn|let|case|if|use|import|pub|type|opaque|const|as|assert|panic|todo|echo)\\b"
- identifier: "\\b(Int|Float|String|Bool|List|Option|Result|Nil|BitArray)\\b"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove Nil from here since it's also in constant.

- statement: "\\b(fn|let|case|if|use|import|pub|type|opaque|const|as|assert|panic|todo|echo)\\b"
- identifier: "\\b(Int|Float|String|Bool|List|Option|Result|Nil|BitArray)\\b"
- constant: "\\b(True|False|Nil)\\b"
- operator: "(\\|>|->|<>|==|!=|<=|>=|&&|\\|\\|)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most syntaxes choose to use statement for operators (to color them red in the default colorscheme). Most built-in colorschemes color operator with the default (white) color. The list is missing assignment and arithmetic operators.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Andriamanitra , thanks for the great feedback! I've pushed a new commit that addresses all your points:

Rule Overlap: Moved the base identifier rule to the top, so the specific type and constant rules below it can correctly overwrite it.

Built-in Types: Removed Nil from the built-in types list (kept it under constants).

Operators: Moved operators to statement so they highlight correctly, and added the missing arithmetic, assignment, and Gleam-specific float operators.

Numbers: Implemented the robust number regexes from the Python syntax file to handle binary, octal, hex, and scientific notation floats.

Attributes: Added a preproc rule (@[a-zA-Z0-9_]+) to highlight attributes like @external and @deprecated.

Copy link
Contributor

@Andriamanitra Andriamanitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's working pretty good now but I noticed a couple more issues while testing the syntax with the Gleam code from https://learnxinyminutes.com/gleam

  • Number literals with underscores
  • Float comparisons (<., <=., etc.)
  • Escapes in strings (eg. "\"")
  • Tuples (eg. #(1, 2))

I don't think commenting the file is necessary, the comments are mostly redundant or irrelevant.

- statement: "\\b(fn|let|case|if|use|import|pub|type|opaque|const|as|assert|panic|todo|echo)\\b"

# 5. Attributes / Decorators (Added for @external, @deprecated, etc.)
- preproc: "@[a-zA-Z0-9_]+"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too permissive. I'm not sure if Gleam even has anything besides @external and @deprecated right now but if we follow vim's example we should only allow lowercase here: 1

Suggested change
- preproc: "@[a-zA-Z0-9_]+"
- preproc: "@[a-z][a-z_]*"

Footnotes

  1. https://github.com/vim/vim/blob/e21c4a649a830716599eadebad04ddf2393230bc/runtime/syntax/gleam.vim#L65

Comment on lines +25 to +30
# 7. Numbers (Copied directly from Python's robust micro syntax as the maintainer requested)
- constant.number: "\\b0b[01]+\\b"
- constant.number: "\\b0o[0-7]+\\b"
- constant.number: "\\b0x[0-9a-fA-F]+\\b"
- constant.number: "\\b[0-9]+\\.[0-9]*([eE][+-]?[0-9]+)?\\b"
- constant.number: "\\b[0-9]+\\b"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contrary to what the comment claims these are not copied from Python, they're missing support for underscores.

Comment on lines +36 to +37
skip: "\\\\."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
skip: "\\\\."
skip: "\\\\."
rules:
- constant.specialChar: "\\\\."

Copy link
Author

@HiwarkhedePrasad HiwarkhedePrasad Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Andriamanitra, thanks again for the detailed feedback! I've made all the suggested changes:

  • Numbers now support underscores (e.g., 1_000_000)
  • Added float comparison operators (<., >., <=., >=.)
  • String escapes are now highlighted as special characters
  • Added tuple syntax #(...) highlighting
  • Attributes are now restricted to lowercase only

Copy link
Contributor

@Andriamanitra Andriamanitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last commit broke the whole thing by removing the required filetype:.

@HiwarkhedePrasad
Copy link
Author

Sorry about that, fixed in the latest commit — restored the filetype: gleam field and also cleaned up the inline comments while I was at it.

Copy link
Contributor

@Andriamanitra Andriamanitra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything seems to work fine now. The tuple highlighting looks a bit weird because the opening paren is colored but the closing one is not but I guess it's fine.

@JoeKar
Copy link
Member

JoeKar commented Mar 18, 2026

Everything seems to work fine now. The tuple highlighting looks a bit weird because the opening paren is colored but the closing one is not but I guess it's fine.

Maybe we could try it with a region (like in the html.yaml):

    - default:
        start: "#\\("
        end: "\\)"
        limit-group: special
        rules:
            - constant.number: "\\b0b[01](_?[01])*\\b"
            - constant.number: "\\b0o[0-7](_?[0-7])*\\b"
            - constant.number: "\\b0x[0-9a-fA-F](_?[0-9a-fA-F])*\\b"
            - constant.number: "\\b[0-9](_?[0-9])*(\\.[0-9](_?[0-9])*)?([eE][+-]?[0-9](_?[0-9])*)?\\b"
            - constant.string:
                start: '"'
                end: '"'
                skip: "\\\\."
                rules:
                    - constant.specialChar: "\\\\."

Yes, it duplicates rules, but we can't include the same type, because this would lead to recursion.

BTW:
Looks like we've inconsistent (2-space and 4-space) indentation.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants