Skip to content

Commit 1a9ac2b

Browse files
carlwrsgoudham
andauthored
feat(syntax): improve support for Haskell (#559)
Co-authored-by: Hammy <[email protected]>
1 parent 155479e commit 1a9ac2b

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

packages/catppuccin-vsc/src/theme/semanticTokens.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,44 @@ export const getSemanticTokens = (context: ThemeContext): SemanticTokens => {
3434
"type.defaultLibrary:go": { foreground: palette.mauve },
3535
"variable.readonly.defaultLibrary:go": { foreground: palette.mauve },
3636

37+
// Haskell:
38+
"enumMember:haskell": /* data constructors */ {
39+
foreground: palette.blue,
40+
fontStyle: "",
41+
},
42+
"enum:haskell": /* types */ {
43+
foreground: palette.yellow,
44+
fontStyle: context.options.italicKeywords ? "italic" : undefined,
45+
},
46+
"type:haskell": /* type aliases */ {
47+
foreground: palette.yellow,
48+
fontStyle: context.options.italicKeywords ? "italic" : undefined,
49+
},
50+
"class:haskell": /* typeclasses */ {
51+
foreground: palette.yellow,
52+
fontStyle: "",
53+
},
54+
"interface:haskell": /* type families */ {
55+
foreground: palette.pink,
56+
fontStyle: "",
57+
// needs something distinct from typeclasses -> pick pink which is used for meta-variables in Rust
58+
},
59+
"property:haskell": /* getters in data constructors/records */ {
60+
foreground: palette.lavender,
61+
fontStyle: context.options.italicKeywords ? "italic" : undefined,
62+
},
63+
"macro:haskell": /* pattern synonyms */ {
64+
foreground: palette.blue,
65+
fontStyle: "",
66+
},
67+
"typeParameter:haskell": {
68+
foreground: palette.maroon,
69+
fontStyle: "",
70+
},
71+
"variable:haskell": {
72+
fontStyle: "",
73+
},
74+
3775
// TOML syntax
3876
tomlArrayKey: { foreground: palette.blue, fontStyle: "" },
3977
tomlTableKey: { foreground: palette.blue, fontStyle: "" },
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
import type { TextmateColors, ThemeContext } from "@/types";
2+
3+
/*
4+
5+
STYLING CHOICES
6+
---------------
7+
8+
language element ->color ->fontStyle
9+
----------------- ------- -----------
10+
functions blue italic
11+
data constructors blue -
12+
types yellow italic
13+
typeclasses [1] yellow -
14+
15+
It would be preferrable for each listed language element to appear visually distinct from the other. With choices above, that is also achieved, granted setting `italicKeywords` is active.
16+
17+
[1]: typeclasses are not distinguishable from types with only textMate scopes; requires semantic tokens
18+
19+
20+
GRAMMAR REF.
21+
------------
22+
23+
https://github.com/JustusAdam/language-haskell
24+
25+
- is the only Haskell textMate grammar in use
26+
- (is the upstream of the Haskell VS Code extension)
27+
- generated listing of all assigned scopes: https://github.com/JustusAdam/language-haskell/blob/master/scope-lists/haskell.scope-db.yaml
28+
29+
*/
30+
31+
const tokens = (context: ThemeContext): TextmateColors => {
32+
const { palette } = context;
33+
34+
return [
35+
{
36+
name: "data constructors",
37+
scope: [
38+
"meta.declaration.data constant.other.haskell",
39+
"constant.other.haskell", // to hit data constr. if no semantic tokens
40+
"meta.declaration.pattern constant.other.haskell", // pattern synonyms
41+
"constant.language.unit.haskell punctuation",
42+
"constant.language.unit.unboxed.haskell punctuation",
43+
],
44+
settings: {
45+
foreground: palette.blue,
46+
fontStyle: "",
47+
},
48+
},
49+
{
50+
name: "types",
51+
scope: ["storage.type.haskell"],
52+
settings: {
53+
foreground: palette.yellow,
54+
fontStyle: "italic",
55+
},
56+
},
57+
{
58+
name: "`()` unit type (not italicized)",
59+
scope: [
60+
"support.constant.unit.haskell punctuation",
61+
"support.constant.unit.haskell keyword.operator.hash",
62+
"support.constant.unit.unboxed.haskell punctuation",
63+
"support.constant.unit.unboxed.haskell keyword.operator.hash",
64+
],
65+
settings: {
66+
foreground: palette.yellow,
67+
fontStyle: "",
68+
},
69+
},
70+
{
71+
name: "type parameters",
72+
scope: ["variable.other.generic-type.haskell"],
73+
settings: {
74+
foreground: palette.maroon,
75+
fontStyle: "",
76+
},
77+
},
78+
{
79+
name: "special words (builtin constants-like)",
80+
scope: [
81+
"keyword.other.default.haskell",
82+
"keyword.other.role.nominal.haskell",
83+
"keyword.other.role.representational.haskell",
84+
"keyword.other.role.phantom.haskell",
85+
],
86+
settings: {
87+
foreground: palette.red,
88+
},
89+
},
90+
{
91+
name: "pragma specifiers",
92+
scope: [
93+
"keyword.other.preprocessor.haskell",
94+
"keyword.other.preprocessor.pragma.haskell",
95+
],
96+
settings: {
97+
foreground: palette.rosewater,
98+
},
99+
},
100+
{
101+
name: "pragma arguments",
102+
scope: ["keyword.other.preprocessor.extension.haskell"],
103+
settings: {
104+
foreground: palette.red,
105+
},
106+
},
107+
{
108+
name: "C preprocessor directives",
109+
scope: [
110+
"source.haskell meta.preprocessor.c",
111+
"source.haskell meta.preprocessor.c punctuation.definition.preprocessor.c",
112+
],
113+
settings: {
114+
foreground: palette.rosewater,
115+
},
116+
},
117+
{
118+
name: "Haskell preprocessor directives",
119+
scope: ["meta.preprocessor.haskell"],
120+
settings: {
121+
foreground: palette.overlay2,
122+
// the enclosing block comment definitions ({-#, #-}) are scoped only with the `meta` scope -> using the comment color is the best-looking choice (also, the whole thing _is_ technically a block comment)
123+
},
124+
},
125+
{
126+
name: "getters in data constructors/records",
127+
scope: [
128+
"variable.other.member.haskell",
129+
"variable.other.member.definition.haskell",
130+
],
131+
settings: {
132+
foreground: palette.lavender,
133+
fontStyle: "italic",
134+
},
135+
},
136+
{
137+
name: "fix else keyword",
138+
scope: ["keyword.control.else.haskell"],
139+
settings: {
140+
foreground: palette.mauve,
141+
},
142+
// catppuccin default sets to yellow considering it preprocessor-like, which it is not for Haskell
143+
},
144+
{
145+
name: "char literals (are enum variants)",
146+
scope: [
147+
"string.quoted.single.haskell",
148+
"string.quoted.single.haskell punctuation.definition.string",
149+
],
150+
settings: {
151+
foreground: palette.teal,
152+
},
153+
},
154+
{
155+
name: "operators",
156+
scope: [
157+
"storage.type.operator.haskell",
158+
"storage.type.operator.infix.haskell",
159+
"entity.name.function.infix.haskell", // op. in parens, e g: (+)
160+
"punctuation.backtick.haskell", // e g: x `op` y
161+
],
162+
settings: {
163+
foreground: palette.teal,
164+
fontStyle: "",
165+
},
166+
},
167+
{
168+
name: "the , in `(,)`",
169+
scope: [
170+
"support.constant.tuple.haskell",
171+
"support.constant.tuple.unboxed.haskell",
172+
],
173+
settings: {
174+
foreground: palette.teal,
175+
fontStyle: "",
176+
},
177+
// the scoping doesn't allow distinguishing `(,)` in types (= type function) and terms (= data constructor) -> color as an operator since that works OK for both
178+
},
179+
{
180+
name: "a few special symbols",
181+
scope: [
182+
"keyword.operator.lambda.haskell",
183+
"keyword.operator.pipe.haskell",
184+
"keyword.operator.double-dot.haskell",
185+
"variable.other.member.wildcard.haskell",
186+
],
187+
settings: {
188+
foreground: palette.red,
189+
},
190+
// neither operators nor delimiters, and should preferrably appear distinct from both
191+
},
192+
{
193+
name: "delimiter-like",
194+
scope: [
195+
"meta.type-application keyword.operator.prefix.at.haskell",
196+
// symbols in pattern-matching:
197+
"keyword.operator.infix.tight.at.haskell", // @ in pair@(x,y)
198+
"keyword.operator.prefix.tilde.haskell", // ~ in ~(x,y)
199+
"keyword.operator.prefix.bang.haskell",
200+
// delimiters in type signatures:
201+
"keyword.operator.double-colon.haskell",
202+
"keyword.operator.big-arrow.haskell",
203+
// delimiters in type signatures - the . after `forall`:
204+
"meta.function.type-declaration keyword.operator.period.haskell",
205+
"meta.type-declaration keyword.operator.period.haskell",
206+
"meta.declaration.type keyword.operator.period.haskell",
207+
],
208+
settings: {
209+
foreground: palette.overlay2,
210+
fontStyle: "",
211+
},
212+
// these are not operators, but are either delimiter-like, or punctuation that benefits from not drawing visual attention
213+
// -> color as delimiters/overlay2 since: closer to style guide + looks better + improves code readability
214+
},
215+
{
216+
name: "TemplateHaskell and QuasiQuoter (macros)",
217+
scope: [
218+
"keyword.operator.prefix.dollar.haskell",
219+
"keyword.operator.quasi-quotation.begin.haskell",
220+
"keyword.operator.quasi-quotation.end.haskell",
221+
],
222+
settings: {
223+
foreground: palette.pink, // like macros in rust
224+
},
225+
},
226+
{
227+
name: "minus sign in negative num literals",
228+
scope: ["keyword.operator.prefix.minus.haskell"],
229+
settings: {
230+
foreground: palette.peach,
231+
},
232+
},
233+
];
234+
};
235+
236+
export default tokens;

packages/catppuccin-vsc/src/theme/tokens/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import dotenv from "./dotenv";
99
import gdscript from "./gdscript";
1010
import golang from "./golang";
1111
import graphql from "./graphql";
12+
import haskell from "./haskell";
1213
import html from "./html";
1314
import java from "./java";
1415
import javascript from "./javascript";
@@ -297,6 +298,7 @@ export default function tokens(context: ThemeContext): TextmateColors {
297298
gdscript,
298299
golang,
299300
graphql,
301+
haskell,
300302
html,
301303
java,
302304
javascript,

0 commit comments

Comments
 (0)