From cceedd7244a12f7a8c85a9c6a00f3948a946b643 Mon Sep 17 00:00:00 2001 From: FichteFoll Date: Tue, 21 Jul 2020 13:12:03 +0200 Subject: [PATCH] [Haskell] Rewrite Haskell syntax (#2225) * [Haskell] Rewrite operator matching - Use variables - Highlight '*' (and combinations) as operator - Add punctuation scope to infix notation - Scope non-infix notation as `keyword.operator` * [Haskell] Update keyword matching - Add proper scopes to control keywords - Add proper scopes to declarations - Use proper scope names for entities in declarations * [Haskell] Restructure with contexts * [Haskell] Remove usages of double quoted scalars * [Haskell] Simplify string matches Also highlight superfluous characters. * [Haskell] Reduce max line length * [Haskell] Match groups * [Haskell] Adjust scopes for imports Not final due to https://github.com/sublimehq/Packages/issues/1842 being unresolved, but still an improvement. * [Haskell] Match lists * [Haskell] Correctly match idents with trailing ' * [Haskell] More gracious infix operator matching * [Haskell] Adjust keyword scopes to recent standards * [Haskell] match OPTIONS_HADDOCK Same as https://github.com/sublimehq/Packages/pull/2270/files * [Haskell] match deriving (..) via (..) Same as https://github.com/sublimehq/Packages/pull/2271/files * [Haskell] match @ and # in keyword.operator.haskell Same as - https://github.com/sublimehq/Packages/pull/2272/files - https://github.com/sublimehq/Packages/pull/2273/files Co-Authored-By: Nikos Baxevanis * [Haskell] match deriving instance (..) * [Haskell] Match functions from the prelude Based on https://github.com/atom-haskell/language-haskell/blob/e036e449909816e616b880157e2703e70fc9b5df/grammars/haskell.cson#L1306-L1307 Co-Authored-By: Nikos Baxevanis * [Haskell] Add tests for `via` derives * [Haskell] match deriving instance (..) without breaking data deriving This fixes a bug introduced via 0d36dd1b18baa0e57474edc1853d901cd1887880 Co-authored-by: Nikos Baxevanis --- Haskell/Haskell.sublime-syntax | 390 ++++++++++++++++++++++----------- Haskell/syntax_test_haskell.hs | 189 +++++++++++++++- 2 files changed, 449 insertions(+), 130 deletions(-) diff --git a/Haskell/Haskell.sublime-syntax b/Haskell/Haskell.sublime-syntax index e45d67341d4..ef7512149f4 100644 --- a/Haskell/Haskell.sublime-syntax +++ b/Haskell/Haskell.sublime-syntax @@ -5,69 +5,143 @@ name: Haskell file_extensions: - hs scope: source.haskell + +variables: + # In case this regex seems overly general, + # note that Haskell permits the definition of new operators + # which can be nearly any string of punctuation characters, + # such as $%^&*. + operator_char: '[*|!%&$@#?~+:\-.=\\]' + operator_infix: '{{operator_char}}+' + operator_parens: '(?:{{operator_infix}}|,+)' + module_name: (?:[A-Z][A-Za-z._']*) + escape_chars: |- + (?x:NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI + |DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL + |[abfnrtv\\\"'\&]) + escape_sequence: |- + (?x: + (\\{{escape_chars}}) # Escapes + | (\\[0-9]+) # Decimal Escapes + | (\\o[0-7]+) # Octal Escapes + | (\\x[0-9A-Fa-f]+) # Hexadecimal Escapes + | (\^[A-Z@\[\]\\\^_]) # Control Chars + ) + contexts: + prototype: + - include: comment + - include: preprocessor + main: - - match: "(`)[a-zA-Z_']*?(`)" - comment: "In case this regex seems unusual for an infix operator, note that Haskell allows any ordinary function application (elem 4 [1..10]) to be rewritten as an infix expression (4 `elem` [1..10])." - scope: keyword.operator.function.infix.haskell - captures: - 1: punctuation.definition.entity.haskell - 2: punctuation.definition.entity.haskell + - include: statement + - include: expression + + statement: + - include: declaration + - include: import + + expression: + - include: keyword + - include: operator + - include: constant + - include: string + - include: splice + - include: group + - include: list + - include: ident + - match: ',' + scope: punctuation.separator.comma.haskell + + group: + - match: \( + scope: punctuation.section.group.begin.haskell + push: + - meta_scope: meta.group.haskell + - match: \) + scope: punctuation.section.group.end.haskell + pop: true + - include: expression + + list: + - match: \[ + scope: punctuation.section.sequence.begin.haskell + push: + - meta_scope: meta.sequence.haskell + - match: \] + scope: punctuation.section.sequence.end.haskell + pop: true + - match: ',' + scope: punctuation.separator.sequence.haskell + - include: expression + + constant: - match: \(\) scope: constant.language.unit.haskell - match: '\[\]' scope: constant.language.empty-list.haskell - - match: \b(module)\b + - include: number + - match: '\b[A-Z]\w*\b' + scope: constant.other.haskell + + number: + - match: \b(0[oO])[0-7]+\b + scope: constant.numeric.integer.octal.haskell captures: - 1: keyword.other.haskell + 1: punctuation.definition.numeric.base.haskell + - match: \b(0[xX])\h+\b + scope: constant.numeric.integer.hexadecimal.haskell + captures: + 1: punctuation.definition.numeric.base.haskell + - match: \b\d+\b + scope: constant.numeric.integer.decimal.haskell + + declaration: + - match: \bmodule\b + scope: keyword.declaration.namespace.haskell push: - meta_scope: meta.declaration.module.haskell - - match: (where) - captures: - 1: keyword.other.haskell + - match: \bwhere\b + scope: keyword.control.context.haskell pop: true - - include: module_name + - match: \b{{module_name}}\b + scope: entity.name.namespace.haskell - include: module_exports - - match: "[a-z]+" - scope: invalid - - match: \b(class)\b - captures: - 1: keyword.other.haskell + - match: \bclass\b + scope: keyword.declaration.class.haskell push: - meta_scope: meta.declaration.class.haskell - - match: \b(where)\b - captures: - 1: keyword.other.haskell + - match: \bwhere\b + scope: keyword.control.context.haskell pop: true - - match: \b(Mon(ad|oid)|Functor|Applicative|(Folda|Traversa)ble|Eq|Ord|Read|Show|Num|(Frac|Ra)tional|Enum|Bounded|Real(Frac|Float)?|Integral|Floating)\b + - match: |- + \b(?x:Monad|Monadoid|Functor|Applicative|Foldableble|Traversable + |Eq|Ord|Read|Show|Num|Fractional|Rational|Enum|Bounded + |Real|RealFrac|RealFloat|Integral|Floating)\b scope: support.class.prelude.haskell - - match: "[A-Z][A-Za-z_']*" + - match: '[A-Z][A-Za-z_'']*' scope: entity.other.inherited-class.haskell - match: '\b[a-z][a-zA-Z0-9_'']*\b' scope: variable.other.generic-type.haskell - - match: \b(instance)\b - captures: - 1: keyword.other.haskell + - match: \binstance\b + scope: keyword.declaration.haskell push: - meta_scope: meta.declaration.instance.haskell - - match: \b(where)\b|$ - captures: - 1: keyword.other.haskell + - match: \bwhere\b|$ + scope: keyword.control.context.haskell pop: true - include: type_signature - - match: \b(import)\b + - match: '^(\s*)([a-z_][a-zA-Z0-9_'']*|\(({{operator_parens}})\))\s*(::|∷)' captures: - 1: keyword.other.haskell + 2: entity.name.function.haskell + 3: keyword.operator.infix.haskell + 4: keyword.other.double-colon.haskell push: - - meta_scope: meta.import.haskell - - match: ($|;) + - meta_scope: meta.function.type-declaration.haskell + - match: ^(?!\s*(?:--|{-|$)|\1\s) pop: true - - match: (qualified|as|hiding) - scope: keyword.other.haskell - - include: module_name - - include: module_exports - - include: comments - - match: (deriving)\s*\( + - include: type_signature + - match: (deriving|via)\s*\( captures: 1: keyword.other.haskell push: @@ -76,45 +150,113 @@ contexts: pop: true - match: '\b[A-Z][a-zA-Z_'']*' scope: entity.other.inherited-class.haskell - - match: \b(deriving|where|data|type|case|of|let|in|newtype|default)\b + + + import: + - match: \bimport\b + scope: keyword.control.import.haskell + push: + - meta_scope: meta.import.haskell + - match: ($|;) + pop: true + - match: (qualified|as|hiding) + scope: keyword.control.import.haskell + - include: module_name + - include: module_exports + + keyword: + - match: \b(?:do|in)\b + scope: keyword.control.context.haskell + - match: \b(?:newtype|type)\b + scope: keyword.declaration.type.haskell + - match: \b(?:data)\b + scope: keyword.declaration.data.haskell + - match: \b(?:deriving)\b + scope: keyword.declaration.data.haskell + - match: \b(?:case|of)\b + scope: keyword.control.conditional.select.haskell # the construct is commonly called "select" + - match: \b(?:let|where)\b + scope: keyword.declaration.variable.haskell + - match: \breturn\b + scope: keyword.control.flow.return.haskell + - match: \b(?:default|otherwise)\b scope: keyword.other.haskell + - match: \b(?:(if)|(then)|(else))\b + captures: + 1: keyword.control.conditional.if.haskell + 2: keyword.control.conditional.then.haskell + 3: keyword.control.conditional.else.haskell + + operator: + - match: (`)[a-zA-Z_'.\d]+(`) + # Haskell allows any ordinary function application (elem 4 [1..10]) + # to be rewritten as an infix expression (4 `elem` [1..10])." + scope: keyword.operator.function.infix.haskell + captures: + 1: punctuation.definition.function.begin.haskell + 2: punctuation.definition.function.end.haskell - match: '\binfix[lr]?\b' scope: keyword.operator.haskell - - match: \b(do|if|then|else)\b - scope: keyword.control.haskell - match: \b\d+(?:(\.)\d+(?:[eE][-+]?\d+)?|(?:[eE][-+]?\d+))\b scope: constant.numeric.float.decimal.haskell captures: 1: punctuation.separator.decimal.haskell - - match: \b(0[oO])[0-7]+\b - scope: constant.numeric.integer.octal.haskell - captures: - 1: punctuation.definition.numeric.base.haskell - - match: \b(0[xX])\h+\b - scope: constant.numeric.integer.hexadecimal.haskell + - match: '{{operator_infix}}' + scope: keyword.operator.haskell + - include: operator_paren + + operator_paren: + - match: \(({{operator_parens}})\) + scope: variable.function.infix.haskell captures: - 1: punctuation.definition.numeric.base.haskell - - match: \b\d+\b - scope: constant.numeric.integer.decimal.haskell + 1: keyword.operator.haskell + + preprocessor: + # In addition to Haskell's "native" syntax, + # GHC permits the C preprocessor to be run on a source file. - match: ^\s*(#)\s*\w+ - comment: In addition to Haskell's "native" syntax, GHC permits the C preprocessor to be run on a source file. scope: meta.preprocessor.c captures: 1: punctuation.definition.preprocessor.c - include: pragma + + string: - match: '"' scope: punctuation.definition.string.begin.haskell push: + - meta_include_prototype: false - meta_scope: string.quoted.double.haskell - match: $|" scope: punctuation.definition.string.end.haskell pop: true - - match: '\\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\\"''\&])' - scope: constant.character.escape.haskell - - match: '\\o[0-7]+|\\x[0-9A-Fa-f]+|\\[0-9]+' - scope: constant.character.escape.octal.haskell - - match: '\^[A-Z@\[\]\\\^_]' - scope: constant.character.escape.control.haskell + - match: '{{escape_sequence}}' + captures: + 1: constant.character.escape.haskell + 2: constant.character.escape.decimal.haskell + 3: constant.character.escape.octal.haskell + 4: constant.character.escape.hexadecimal.haskell + 5: constant.character.escape.control.haskell + - match: |- + (?x) + (') + (?: + [\ -\[\]-~] # Basic Char + | {{escape_sequence}} # Escapes + ) + ([^']*) + (') + scope: string.quoted.single.haskell + captures: + 1: punctuation.definition.string.begin.haskell + 2: constant.character.escape.haskell + 3: constant.character.escape.decimal.haskell + 4: constant.character.escape.octal.haskell + 5: constant.character.escape.hexadecimal.haskell + 6: constant.character.escape.control.haskell + 7: invalid.illegal.expected-closing-quotation.haskell + 8: punctuation.definition.string.end.haskell + + splice: - match: '\[(?:|e|d|t|p)\|' comment: Points out splices in ast quotes scope: keyword.other.quasibracket.haskell @@ -129,7 +271,7 @@ contexts: scope: keyword.other.splice.haskell - match: \$ scope: string.quasiquoted.haskell - - match: "[^$]*" + - match: '[^$]*' scope: string.quasiquoted.haskell - match: \$\( comment: Highlight the beginning of a splice. @@ -145,45 +287,50 @@ contexts: pop: true - match: .* scope: string.quasiquoted.haskell + + ident: - match: |- - (?x) - (') - (?: - [\ -\[\]-~] # Basic Char - | (\\(?:NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE - |DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS - |US|SP|DEL|[abfnrtv\\\"'\&])) # Escapes - | (\\o[0-7]+) # Octal Escapes - | (\\x[0-9A-Fa-f]+) # Hexadecimal Escapes - | (\^[A-Z@\[\]\\\^_]) # Control Chars - ) - (') - scope: string.quoted.single.haskell - captures: - 1: punctuation.definition.string.begin.haskell - 2: constant.character.escape.haskell - 3: constant.character.escape.octal.haskell - 4: constant.character.escape.hexadecimal.haskell - 5: constant.character.escape.control.haskell - 6: punctuation.definition.string.end.haskell - - match: '^(\s*)([a-z_][a-zA-Z0-9_'']*|\([|!%$+\-.,=]+\))\s*(::|∷)' - captures: - 2: entity.name.function.haskell - 3: keyword.other.double-colon.haskell + (?x) \b + (abs|acos|acosh|all|and|any|appendFile|asTypeOf|asin|asinh|atan|atan2|atanh + |break + |ceiling|compare|concat|concatMap|const|cos|cosh|curry|cycle + |decodeFloat|div|divMod|drop|dropWhile + |either|elem|encodeFloat|enumFrom|enumFromThen|enumFromThenTo|enumFromTo|error + |errorWithoutStackTrace|even|exp|exponent + |fail|filter|flip|floatDigits|floatRadix|floatRange|floor|fmap|foldMap|foldl|foldl1|foldr + |foldr1|fromEnum|fromInteger|fromIntegral|fromRational|fst + |gcd|getChar|getContents|getLine + |head + |id|init|interact|ioError|isDenormalized|isIEEE|isInfinite|isNaN|isNegativeZero|iterate + |last|lcm|length|lex|lines|log|logBase|lookup + |map|mapM|mapM_|mappend|max|maxBound|maximum|maybe|mconcat|mempty|min|minBound|minimum|mod + |negate|not|notElem|null + |odd|or|otherwise + |pi|pred|print|product|properFraction|pure|putChar|putStr|putStrLn + |quot|quotRem|read + |readFile|readIO|readList|readLn|readParen|reads|readsPrec|realToFrac|recip|rem|repeat + |replicate|return|reverse|round|scaleFloat + |scanl|scanl1|scanr|scanr1|seq|sequence|sequenceA|sequence_|show|showChar|showList + |showParen|showString|shows|showsPrec|significand|signum|sin|sinh|snd|span|splitAt|sqrt + |subtract|succ|sum|tail + |take|takeWhile|tan|tanh|toEnum|toInteger|toRational|traverse|truncate|uncurry + |undefined|unlines|until|unwords|unzip|unzip3|userError|words + |writeFile + |zip|zip3|zipWith|zipWith3 + ) \b + scope: support.function.prelude.haskell + - match: '[[:lower:]][^\s{{operator_char}}),\]{}]+' + scope: meta.name.haskell + + comment: + - match: '--' + scope: punctuation.definition.comment.haskell push: - - meta_scope: meta.function.type-declaration.haskell - - match: ^(?!\s*(?:--|{-|$)|\1\s) + - meta_scope: comment.line.double-dash.haskell + - match: $\n? pop: true - - include: type_signature - - match: '\b[A-Z]\w*\b' - scope: constant.other.haskell - - include: comments - - include: infix_op - - match: '[|!%$?~+:\-.=\\]+' - comment: In case this regex seems overly general, note that Haskell permits the definition of new operators which can be nearly any string of punctuation characters, such as $%^&*. - scope: keyword.operator.haskell - - match: "," - scope: punctuation.separator.comma.haskell + - include: block_comment + block_comment: - match: '\{-(?!#)' scope: punctuation.definition.comment.begin.haskell @@ -198,51 +345,35 @@ contexts: - match: '-\}' scope: punctuation.definition.comment.end.haskell pop: true - comments: - - match: '--' - scope: punctuation.definition.comment.haskell - push: - - meta_scope: comment.line.double-dash.haskell - - match: $\n? - pop: true - - include: block_comment - infix_op: - - match: \([-+*/,|!%$:.=<>]+\) - scope: entity.name.function.infix.haskell + module_exports: - match: \( + scope: punctuation.section.group.begin.haskell push: - meta_scope: meta.declaration.exports.haskell - match: \) + scope: punctuation.section.group.end.haskell pop: true - match: '\b[a-z][a-zA-Z_''0-9]*' - scope: entity.name.function.haskell + scope: variable.function.haskell - match: '\b[A-Z][A-Za-z_''0-9]*' scope: storage.type.haskell - - match: "," + - match: ',' scope: punctuation.separator.comma.haskell - - include: infix_op + - include: operator_paren - match: \(.*?\) comment: So named because I don't know what to call this. scope: meta.other.unknown.haskell - - include: comments + module_name: - - match: "[A-Z][A-Za-z._']*" + - match: '\b{{module_name}}\b' scope: support.other.module.haskell - pragma: - - match: '\{-#' - push: - - meta_scope: meta.preprocessor.haskell - - match: '#-\}' - pop: true - - match: \b(LANGUAGE|OPTIONS_GHC|INCLUDE|WARNING|DEPRECATED|MINIMAL|UNPACK|NOUNPACK|SOURCE|OVERLAPPING|OVERLAPPABLE|OVERLAPS|INCOHERENT|INLINE|NOINLINE|INLINABLE|CONLIKE|LINE|RULES|SPECIALIZE|SPECIALISE)\b - # https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#pragmas - scope: keyword.other.preprocessor.haskell + type_signature: - include: pragma - - match: "(->|→)" + - match: '(?:->|→)' scope: keyword.other.arrow.haskell - - match: "(=>|⇒)" + - match: '(?:=>|⇒)' scope: keyword.other.big-arrow.haskell - match: '\b[a-z][a-zA-Z0-9_'']*\b' scope: variable.other.generic-type.haskell @@ -250,4 +381,19 @@ contexts: scope: storage.type.haskell - match: \(\) scope: support.constant.unit.haskell - - include: comments + + pragma: + - match: '\{-#' + push: + - meta_scope: meta.preprocessor.haskell + - match: '#-\}' + pop: true + - match: |- + \b(?x: + LANGUAGE|OPTIONS_GHC|OPTIONS_HADDOCK|INCLUDE|WARNING|DEPRECATED|MINIMAL + |UNPACK|NOUNPACK|SOURCE|OVERLAPPING|OVERLAPPABLE|OVERLAPS + |INCOHERENT|INLINE|NOINLINE|INLINABLE|CONLIKE|LINE|RULES + |SPECIALIZE|SPECIALISE + )\b + # https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#pragmas + scope: keyword.other.preprocessor.haskell diff --git a/Haskell/syntax_test_haskell.hs b/Haskell/syntax_test_haskell.hs index 8758f9a3b9d..c779633a98b 100644 --- a/Haskell/syntax_test_haskell.hs +++ b/Haskell/syntax_test_haskell.hs @@ -42,20 +42,49 @@ -- ^^ punctuation.definition.comment.end.haskell -- ^ - comment.block.haskell +--DECLARATIONS + + module Name where +-- ^^^^^^^^^^^^^^^^^ meta.declaration.module.haskell +-- ^^^^^^ keyword.declaration.namespace.haskell +-- ^^^^ entity.name.namespace.haskell +-- ^^^^^ keyword.control.context.haskell + class (Functor t, Foldable t) => Traversable t where --- ^^^^^ keyword.other.haskell -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.class.haskell +-- ^^^^^ keyword.declaration.class.haskell +-- ^^^^^ keyword.control.context.haskell {-# MINIMAL traverse | sequenceA LANGUAGE #-} -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.preprocessor.haskell -- ^ - meta.preprocessor.haskell -- ^^^^^^^ keyword.other.preprocessor.haskell + {-# OPTIONS_HADDOCK not-home #-} +-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.preprocessor.haskell +-- ^ - meta.preprocessor.haskell +-- ^^^^^^^^^^^^^^^ keyword.other.preprocessor.haskell + -- | Map each element of a structure to an action, -- evaluate these actions from left to right, and -- collect the results. For a version that ignores -- the results see 'Data.Foldable.traverse_'. -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.line.double-dash.haskell + data Record = + Record { + recordInt :: Int + , recordString :: String + , recordDouble :: Double + , recordRational :: Rational + } deriving (Eq, Ord, Generic) +-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.deriving.haskell + deriving (Read, Show) via (Quiet Record) +-- ^^^ keyword.other.haskell +-- ^^^^^^^^^^^^^^^^^^ meta.deriving.haskell +-- ^^^^^ entity.other.inherited-class.haskell +-- ^ - entity +-- ^^^^^^ entity.other.inherited-class.haskell + traverse :: Applicative f => -- ^^^^^^^^ entity.name.function.haskell -- ^^ keyword.other.double-colon.haskell @@ -88,26 +117,38 @@ sequenceA = traverse id -- ^ keyword.operator.haskell --- --- infix operators --- + +-- INFIX OPERATORS + a a = (+) a 2 -- ^ keyword.operator.haskell --- ^^^ entity.name.function.infix.haskell +-- ^^^ variable.function.infix.haskell +-- ^ keyword.operator.haskell -- ^ constant.numeric.integer.decimal.haskell a a = (-) a 2 -- ^ keyword.operator.haskell --- ^^^ entity.name.function.infix.haskell +-- ^^^ variable.function.infix.haskell -- ^ constant.numeric.integer.decimal.haskell a a = (*) a 2 -- ^ keyword.operator.haskell --- ^^^ entity.name.function.infix.haskell +-- ^^^ variable.function.infix.haskell -- ^ constant.numeric.integer.decimal.haskell a a = (/) a 2 -- ^ keyword.operator.haskell --- ^^^ entity.name.function.infix.haskell +-- ^^^ variable.function.infix.haskell -- ^ constant.numeric.integer.decimal.haskell + + a `member` x +-- ^^^^^^^^ keyword.operator.function.infix.haskell +-- ^ punctuation.definition.function.begin.haskell +-- ^ punctuation.definition.function.end.haskell + a `P.atan2` x +-- ^^^^^^^^^ keyword.operator.function.infix.haskell +-- ^ punctuation.definition.function.begin.haskell +-- ^ punctuation.definition.function.end.haskell + + -- Tests for #1320, #1880. class TooMany a where @@ -120,6 +161,8 @@ instance TooMany Int where -- ^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.declaration.instance.haskell +-- ^^^^^^^^ keyword.declaration..haskell +-- ^^^^^ keyword.control.context.haskell tooMany n = n > 42 foldBoolGuard :: a -> a -> Bool -> a @@ -145,6 +188,80 @@ countTheBeforeVowel = undefined +--IDENTS + + genericIdent +-- ^ meta.name.haskell + map (flip (/)) [1..] +-- ^^^ support.function.prelude.haskell +-- ^^^^ meta.group.haskell support.function.prelude.haskell + + +--KEYWORDS + +import qualified Data.Vector.Mutable as MutableVector +-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.import.haskell +-- ^^^ keyword.control.import.haskell +-- ^^^^^^^^^ keyword.control.import.haskell +-- ^^^^^^^^^^^^^^^^^^^ support.other.module.haskell +-- ^^ keyword.control.import.haskell +-- ^^^^^^^^^^^^^ support.other.module.haskell +import Data.List.Split (splitOn) +-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.import.haskell +-- ^^^ keyword.control.import.haskell +-- ^^^^^^^^^^^^^^^ support.other.module.haskell +-- ^^^^^^^^^ meta.declaration.exports.haskell +-- ^ punctuation.section.group.begin.haskell +-- ^^^^^^^ variable.function.haskell +-- ^ punctuation.section.group.end.haskell + + deriving instance FromJSON Amount +-- ^^^^^^^^ keyword.declaration.data.haskell + deriving instance FromJSON Ask +-- ^^^^^^^^ meta.declaration.instance.haskell keyword.declaration.haskell + +test = +-- ^ keyword.operator.haskell + let x = 2 in x * y +-- ^^^ keyword.declaration.variable.haskell +-- ^^ keyword.control.context.haskell + where +-- ^^^^^ keyword.declaration.variable.haskell + y = 1 +-- ^ keyword.operator.haskell + +test a = case a of +-- ^^^^ keyword.control.conditional.select.haskell +-- ^^ keyword.control.conditional.select.haskell + Nothing -> 0 + Just n -> if n > 0 +-- ^^ keyword.control.conditional.if.haskell + then n +-- ^^^^ keyword.control.conditional.then.haskell + else 0 +-- ^^^^ keyword.control.conditional.else.haskell + +main = do +-- ^^ keyword.control.context.haskell + return () +-- ^^^^^^ keyword.control.flow.return.haskell + +--MISC + + (group) +-- ^^^^^^^ meta.group.haskell +-- ^ punctuation.section.group.begin.haskell +-- ^ punctuation.section.group.end.haskell + + [1,2] +-- ^^^^^ meta.sequence.haskell +-- ^ punctuation.section.sequence.begin.haskell +-- ^ constant.numeric.integer.decimal.haskell +-- ^ punctuation.separator.sequence.haskell +-- ^ constant.numeric.integer.decimal.haskell +-- ^ punctuation.section.sequence.end.haskell + + --NUMBERS 0 @@ -186,3 +303,59 @@ 0XdeafBEEF42 -- ^^^^^^^^^^^^ constant.numeric.integer.hexadecimal -- ^^ punctuation.definition.numeric.base + +--STRINGS + + + 'ab' +-- ^^^^ string.quoted.single.haskell +-- ^ punctuation.definition.string.begin.haskell +-- ^ invalid.illegal.expected-closing-quotation.haskell +-- ^ punctuation.definition.string.end.haskell + + '\129x' +-- ^^^^ string.quoted.single.haskell +-- ^^^^ constant.character.escape.decimal.haskell +-- ^ invalid.illegal.expected-closing-quotation.haskell +-- ^ punctuation.definition.string.end.haskell + + "\o129x\NUL" +-- ^^^^^^^^^^^^ string.quoted.double.haskell +-- ^^^^ constant.character.escape.octal.haskell +-- ^ - constant +-- ^ punctuation.definition.string.end.haskell +-- ^^^^ constant.character.escape.haskell + + a' = b' +-- ^^ meta.name.haskell - string + + +-- Infix operators in context + + data Outrageous = + Flipper Record + | Int :! Int + | Double :@ Double +-- ^ keyword.operator.haskell + | Int `Quux` Double + | String :# Record +-- ^ keyword.operator.haskell + | Simple :$ Outrageous + | DontDoThis { outrageousInt :: Int, outrageousString :: String } + deriving (Eq, Ord, Generic) + deriving (Read, Show) via (Quiet Outrageous) + + genOutrageous :: Gen Outrageous + genOutrageous = + Gen.recursive Gen.choice [ + Flipper <$> genRecord + , (:!) <$> genInt <*> genInt + , (:@) <$> genDouble <*> genDouble +-- ^^ meta.sequence.haskell variable.function.infix.haskell keyword.operator.haskell + , Quux <$> genInt <*> genDouble + , (:#) <$> genString <*> genRecord +-- ^^ meta.sequence.haskell variable.function.infix.haskell keyword.operator.haskell + , DontDoThis <$> genInt <*> genString + ] [ + Gen.subtermM genOutrageous (\x -> (:$) <$> genSimple <*> pure x) + ]