Mirrored from codeberg

git clone https://codeberg.org/pranshu/haskell-ts-mode.git

Jump to: README.md haskell-ts-mode.el


README.md

1	
2	# haskell-ts-mode
3	
4	A [Haskell](https://www.haskell.org/) mode that uses [Tree-sitter](https://tree-sitter.github.io/tree-sitter/).
5	
6	![img](./ss.png)
7	
8	The above screenshot is indented and coloured using `haskell-ts-mode`, with
9	`prettify-symbols-mode` enabled.
10	
11	# Usage
12	
13	-   `C-c C-r` Open REPL
14	-   `C-c C-c` Send code to REPL
15	-   `M-q`   Indent the function
16	
17	# Features
18	
19	Say it with me: Indentation does not change the syntax tree. This means that the
20	indentation is a lot more predictable, but sometimes you must manually press
21	`M-i` to indent.
22	
23	Overview of features:
24	
25	-   Syntax highlighting
26	-   Structural navigation
27	-   Indentation
28	-   Imenu support
29	-   REPL (`C-c C-r` in the mode to run)
30	-   Prettify Symbols mode support
31	
32	# Comparison with `haskell-mode`
33	
34	The more interesting features are:
35	
36	-   Logical syntax highlighting:
37	    -   Only arguments that can be used in functions are highlighted, e.g., in `f
38	            (_:(a:[]))` only `a` is highlighted, as it is the only variable that is
39	        captured, and that can be used in the body of the function.
40	    -   The return type of a function is highlighted.
41	    -   All new variabels are (or should be) highlighted, this includes generators,
42	        lambda arguments.
43	    -   Highlighting the `=` operator in guarded matches correctly, this would be
44	        stupidly hard in regexp based syntax.
45	-   More performant, this is especially seen in longer files.
46	-   Much, much less code, `haskell-mode` has accumlated 30,000 lines of code and
47	    features to do with all things Haskell related. `haskell-ts-mode` just keeps
48	    the scope to basic major mode stuff, and leaves other stuff to external
49	    packages.
50	
51	# Motivation
52	
53	`haskell-mode` contains nearly 30k lines of code, and is about 30 years old. A
54	lot of features implemented by `haskell-mode` are now also available in standard
55	Emacs, and have thus become obsolete.
56	
57	In 2018, a mode called [`haskell-tng-mode`](https://elpa.nongnu.org/nongnu/haskell-tng-mode.html) was made to solve some of these
58	problems. However, because of Haskell's syntax, it too became very complex and
59	required a web of dependencies.
60	
61	Both these modes ended up practically parsing Haskell's syntax to implement
62	indentation, so I thought why not use Tree-sitter?
63	
64	# Structural navigation
65	
66	This mode provides strucural navigation, for Emacs 30+.
67	
68	    combs (x:xs) = map (x:) c ++ c
69	      where c = combs xs
70	
71	In the above code, if the pointer is right in front of the function
72	definition `combs`, and you press `C-M-f` (`forward-sexp`), it will take you to
73	the end of the second line.
74	
75	# Installation
76	
77	Add this into your init.el:
78	```lisp
79	(use-package haskell-ts-mode
80	  :ensure t
81	  :custom
82	  (haskell-ts-font-lock-level 4)
83	  (haskell-ts-use-indent t)
84	  (haskell-ts-ghci "ghci")
85	  (haskell-ts-use-indent t)
86	  :config
87	  (add-to-list 'treesit-language-source-alist
88	   '(haskell . ("https://github.com/tree-sitter/tree-sitter-haskell" "v0.23.1")))
89	  (unless (treesit-grammar-location 'haskell)
90	   (treesit-install-language-grammar 'haskell)))
91	```
92	
93	That is all.  This will install the grammars if not already installed.
94	However, you might need to update the grammar version in the future.
95	
96	## Other recommended packages
97	
98	Unlike `haskell-mode`, this mode has a limited scope to just worrying
99	about haskell.  There are other packages that I find help a lot with
100	development:
101	- [consult-hoogle](https://codeberg.org/rahguzar/consult-hoogle) great
102	  interface for `hoogle`.
103	- [dante](https://github.com/jyp/dante)
104	- [hindent](https://github.com/mihaimaruseac/hindent) If you want to
105	  outsource the indentation and formatting to another haskell package.
106	- [ormolu](https://github.com/vyorkin/ormolu.el) hindent alternative
107	- [hcel](https://github.com/emacsmirror/hcel) Codebase navigator, if
108	  you want a lighter alternaitve to a full blown LSP.
109	
110	# Customization
111	
112	## How to disable `haskell-ts-mode` indentation
113	
114	    (setq haskell-ts-use-indent nil)
115	
116	
117	
118	## Pretify Symbols mode
119	
120	`prettify-symbols-mode` can be used to replace common symbols with
121	unicode alternatives.
122	
123	    (add-hook 'haskell-ts-mode 'prettify-symbols-mode)
124	
125	## Adjusting font lock level
126	
127	Set `haskell-ts-font-lock-level` accordingly.  Default value is 4, so if
128	you suffer from contagious dehydration, you can lower it.
129	
130	## Language server
131	
132	`haskell-ts-mode` works with `lsp-mode` and, since Emacs 30, with
133	`eglot`.
134	
135	To add `eglot` support on Emacs 29 and earlier, add the following code
136	to your `init.el`:
137	
138	    (with-eval-after-load 'eglot
139	      (defvar eglot-server-programs)
140	      (add-to-list 'eglot-server-programs
141	                   '(haskell-ts-mode . ("haskell-language-server-wrapper" "--lsp"))))
142	
143	## Prettify sybmols mode
144	
145	Turning on `prettify-symbols-mode` does stuff like turn `->` to `→`. If you
146	want to prettify words, set `haskell-ts-prettify-words` to non-nil.
147	This will do stuff like prettify `forall` into `∀` and `elem` to `∈`.
148	
149	# TODO 
150	
151	-   Support for M-x align, so that calling it will align all the 'equal'
152	    signs in a region.
153	-   Imenu support for functions with multiple definitions.
154	
155	

haskell-ts-mode.el

1	;;; haskell-ts-mode.el --- A treesit based major mode for haskell -*- lexical-binding:t -*-
2	
3	;; Copyright (C) 2024, 2025 Pranshu Sharma
4	
5	;; Author: Pranshu Sharma <pranshu@bauherren.ovh>
6	;; URL: https://codeberg.org/pranshu/haskell-ts-mode
7	;; Package-Requires: ((emacs "29.3"))
8	;; Version: 1.1.3
9	;; Keywords: languages, haskell
10	
11	;; This program is free software; you can redistribute it and/or modify
12	;; it under the terms of the GNU General Public License as published by
13	;; the Free Software Foundation, either version 3 of the License, or
14	;; (at your option) any later version.
15	
16	;; This program is distributed in the hope that it will be useful,
17	;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18	;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19	;; GNU General Public License for more details.
20	
21	;; You should have received a copy of the GNU General Public License
22	;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
23	
24	;;; Commentary:
25	
26	;; This is a major mode that uses treesitter to provide all the basic
27	;; major mode stuff, like indentation, font lock, etc...
28	;; It uses the grammer at: https://github.com/tree-sitter/tree-sitter-haskell
29	
30	;;; Code:
31	
32	(require 'comint)
33	(require 'treesit)
34	
35	(declare-function treesit-parser-create "treesit.c")
36	(declare-function treesit-node-start "treesit.c")
37	(declare-function treesit-node-parent "treesit.c")
38	(declare-function treesit-node-prev-sibling "treesit.c")
39	(declare-function treesit-node-next-sibling "treesit.c")
40	(declare-function treesit-node-end "treesit.c")
41	(declare-function treesit-node-child "treesit.c")
42	(declare-function treesit-node-type "treesit.c")
43	
44	(defgroup haskell-ts-mode nil
45	  "Group that contains haskell-ts-mode variables"
46	  :group 'langs)
47	
48	(defcustom haskell-ts-ghci "ghci"
49	  "The command to be called to run ghci."
50	  :type 'string)
51	
52	(defcustom haskell-ts-ghci-buffer-name "Inferior Haskell"
53	  "Buffer name for the ghci prcoess."
54	  :type 'string)
55	
56	(defcustom haskell-ts-use-indent t
57	  "Set to non-nil to use the indentation provided by haskell-ts-mode"
58	  :type 'boolean)
59	
60	(defcustom haskell-ts-font-lock-level 4
61	  "Level of font lock, 1 for minimum highlghting and 4 for maximum."
62	  :type '(choice (const :tag "Minimal Highlighting" 1)
63	                 (const :tag "Low Highlighting" 2)
64	                 (const :tag "High Highlighting" 3)
65	                 (const :tag "Maximum Highlighting" 4)))
66	
67	(defcustom haskell-ts-prettify-words nil
68	  "Prettify some words to unicode symbols.
69	This will concat `haskell-ts-prettify-words-alist' to
70	`prettify-symbols-alist' in `haskell-ts-mode'."
71	  :type 'boolean)
72	
73	(defvar haskell-ts-font-lock-feature-list
74	  `((comment str pragma parens)
75	    (type definition function args module import operator)
76	    (match keyword)
77	    (otherwise signature type-sig)))
78	
79	(defvar haskell-ts-prettify-symbols-alist
80	  '(("\\" . "λ")
81	    ("/=" . "≠")
82	    ("->" . "→")
83	    ("=>" . "⇒")
84	    ("<-" . "←")
85	    ("<=" . "≤")
86	    (">=" . "≥")
87	    ("/<" . "≮")
88	    ("/>" . "≯")
89	    ("&&" . "∧")
90	    ("||" . "∨")
91	    ("==" . "≡"))
92	  "`prettify-symbols-alist' for `haskell-ts-mode'.
93	This variable contains all the symbol for `haskell-ts-mode' to unicode
94	character.  See `haskell-ts-prettify-words-alist' for mappign words to
95	alternative unicode character.")
96	
97	(defvar haskell-ts-prettify-words-alist
98	  '(("forall"           . "∀")
99	    ("exists"           . "∃")
100	    ("elem"             . "∈")
101	    ("notElem"          . "∉")
102	    ("member"           . "∈")
103	    ("notMember"        . "∉")
104	    ("union"            . "∪")
105	    ("intersection"     . "∩")
106	    ("isSubsetOf"       . "⊆")
107	    ("isProperSubsetOf" . "⊂")
108	    ("mempty"           . "∅"))
109	  "Additional symbols to prettify for `haskell-ts-mode'.
110	This is added to `prettify-symbols-alist' for `haskell-ts-mode' buffers
111	when `haskell-ts-prettify-words' is non-nil.")
112	
113	(defvar haskell-ts-font-lock
114	  (treesit-font-lock-rules
115	   :language 'haskell
116	   :feature 'keyword
117	   `(["module" "import" "data" "let" "where" "case" "type"
118	      "if" "then" "else" "of" "do" "in" "instance" "class" "newtype"]
119	     @font-lock-keyword-face)
120	   :language 'haskell
121	   :feature 'otherwise
122	   :override t
123	   `(((match (guards guard: (boolean (variable) @font-lock-keyword-face)))
124	      (:match "otherwise" @font-lock-keyword-face)))
125	
126	   ;; This needs to be positioned above where we apply
127	   ;; font-lock-operator-face to comma
128	   :language 'haskell
129	   :override t
130	   :feature 'signature
131	   '((signature (function) @haskell-ts--fontify-type)
132	     (context (function) @haskell-ts--fontify-type)
133	     (signature "::" @font-lock-operator-face))
134	
135	   ;; TODO: It is weird that we use operator face for parenthesses and also for operators.
136	   ;;   I see two other, possibly better solutions:
137	   ;;   1. Use delimiter face for parenthesses, ::, -> and similar, and operator face for operators.
138	   ;;   2. Keep using operator face for parenthesses and co, but use
139	   ;;   function call face for operators (since they are functions at
140	   ;;   the end).
141	   :language 'haskell
142	   :feature 'operator
143	   :override t
144	   '((operator) @font-lock-operator-face
145	     "," @font-lock-operator-face)
146	
147	   :language 'haskell
148	   :feature 'module
149	   '((module (module_id) @font-lock-type-face))
150	
151	   :language 'haskell
152	   :feature 'import
153	   '((import ["qualified" "as" "hiding"] @font-lock-keyword-face))
154	
155	   :language 'haskell
156	   :feature 'type-sig
157	   '((signature (binding_list (variable) @font-lock-doc-markup-face))
158	     (signature (variable) @font-lock-doc-markup-face))
159	
160	   :language 'haskell
161	   :feature 'args
162	   :override 'keep
163	   '((function (infix left_operand: (_) @haskell-ts--fontify-arg))
164	     (function (infix right_operand: (_) @haskell-ts--fontify-arg))
165	     (generator :anchor (_) @haskell-ts--fontify-arg)
166	     (patterns) @haskell-ts--fontify-arg)
167	
168	   :language 'haskell
169	   :feature 'type
170	   '((type) @font-lock-type-face
171	     (constructor) @font-lock-type-face
172	     (declarations (type_synomym (name) @font-lock-type-face))
173	     (declarations (data_type name: (name) @font-lock-type-face))
174	     (declarations (newtype name: (name) @font-lock-type-face))
175	     (deriving "deriving" @font-lock-keyword-face (name) @font-lock-type-face)
176	     (deriving_instance "deriving" @font-lock-keyword-face name: (_) @font-lock-type-face))
177	
178	   :language 'haskell
179	   :feature 'match
180	   `((match ("|" @font-lock-doc-face) ("=" @font-lock-doc-face))
181	     (list_comprehension ("|" @font-lock-doc-face
182	                          (qualifiers (generator "<-" @font-lock-doc-face))))
183	     (match ("->" @font-lock-doc-face)))
184	
185	   :language 'haskell
186	   :override t
187	   :feature 'comment
188	   `(((comment) @font-lock-comment-face)
189	     ((haddock) @font-lock-doc-face))
190	
191	   :language 'haskell
192	   :feature 'pragma
193	   `((pragma) @font-lock-preprocessor-face
194	     (cpp) @font-lock-preprocessor-face)
195	
196	   :language 'haskell
197	   :feature 'str
198	   :override t
199	   `((char) @font-lock-string-face
200	     (string) @font-lock-string-face
201	     (quasiquote (quoter) @font-lock-type-face)
202	     (quasiquote (quasiquote_body) @font-lock-preprocessor-face))
203	
204	   :language 'haskell
205	   :feature 'parens
206	   :override t
207	   `(["(" ")" "[" "]"] @font-lock-operator-face
208	     (infix operator: (_) @font-lock-operator-face))
209	
210	   :language 'haskell
211	   :feature 'function
212	   :override t
213	   '((function name: (variable) @font-lock-function-name-face)
214	     (function (infix (operator)  @font-lock-function-name-face))
215	     (function (infix (infix_id (variable) @font-lock-function-name-face)))
216	     (bind :anchor (_) @haskell-ts--fontify-params)
217	     (function arrow: _ @font-lock-operator-face)))
218	  "The treesitter font lock settings for haskell.")
219	
220	(defun haskell-ts--stand-alone-parent (_ parent bol)
221	  (save-excursion
222	    (goto-char (treesit-node-start parent))
223	    (let ((type (treesit-node-type parent)))
224	      (if (and (not bol)
225	               (or (looking-back "^[ \t]*" (line-beginning-position))
226	                   (member
227	                    type
228	                    '("when" "where" "do" "let" "local_binds" "function"))))
229	          (treesit-node-start parent)
230	        (haskell-ts--stand-alone-parent 1 (funcall
231	                                           (if bol #'treesit-node-parent #'identity)
232	                                           (treesit-node-parent parent))
233	                                        nil)))))
234	
235	(defvar haskell-ts--ignore-types
236	  (regexp-opt '("comment" "cpp" "haddock"))
237	  "Node types that will be ignored by indentation.")
238	
239	(defvar haskell-ts-indent-rules
240	  (let* ((p-sib
241	          (lambda (node &optional arg)
242	            (let* ((func (if arg
243	                             #'treesit-node-prev-sibling
244	                           #'treesit-node-next-sibling))
245	                   (n (funcall func node)))
246	              (while (and n (string-match haskell-ts--ignore-types
247	                                          (treesit-node-type n)))
248	                (setq n (funcall func n)))
249	              n)))
250	         (p-prev-sib
251	          (lambda (node &optional _ _) (treesit-node-start (funcall p-sib node t))))
252	         (p-n-prev (lambda (node) (funcall p-sib node t)))
253	         (parent-first-child (lambda (_ parent _)
254	                               (treesit-node-start (treesit-node-child parent 0)))))
255	    `((haskell
256	       ((node-is "^cpp$") column-0 0)
257	       ((parent-is "^comment$") column-0 0)
258	       ((parent-is "^haddock$") column-0 0)
259	       ((parent-is "^imports$") column-0 0)
260	       ;; Infix
261	       ((n-p-gp nil "infix" "infix")
262	        (lambda (_ node _)
263	          (let ((first-inf nil))
264	            (while (string= "infix"
265	                            (treesit-node-type
266	                             (setq node (treesit-node-parent node))))
267	              (setq first-inf node))
268	            (funcall ,parent-first-child nil first-inf nil)))
269	        0)
270	       ((node-is "^infix$") ,parent-first-child 0)
271	
272	       ;; Lambda
273	       ((parent-is "^lambda\\(_case\\)?$") standalone-parent 2)
274	
275	       ((parent-is "^class_declarations$") prev-sibling 0)
276	
277	       ((node-is "^where$") parent 2)
278	
279	       ;; in
280	       ((node-is "^in$") parent 0)
281	
282	       ((parent-is "qualifiers") parent 0)
283	
284	       ;; list
285	       ((node-is "^]$") parent 0)
286	       ((parent-is "^list$") standalone-parent 2)
287	
288	       ;; If then else
289	       ((node-is "^then$") parent 2)
290	       ((node-is "^else$") parent 2)
291	
292	       ((parent-is "^apply$") haskell-ts--stand-alone-parent 2)
293	       ((node-is "^quasiquote$") grand-parent 2)
294	       ((parent-is "^quasiquote_body$") (lambda (_ _ c) c) 0)
295	       ((lambda (node parent bol)
296	          (when-let ((n (treesit-node-prev-sibling node)))
297	            (while (string= "comment" (treesit-node-type n))
298	              (setq n (treesit-node-prev-sibling n)))
299	            (string= "do" (treesit-node-type n))))
300	        haskell-ts--stand-alone-parent
301	        3)
302	       ((parent-is "^do$") ,p-prev-sib 0)
303	
304	       ((parent-is "^alternatives$") ,p-prev-sib 0)
305	
306	       ;; prev-adaptive-prefix is broken sometimes
307	       (no-node
308	        (lambda (_ _ _)
309	          (save-excursion
310	            (goto-char (line-beginning-position 0))
311	            (back-to-indentation)
312	            (if (looking-at "\n")
313			0
314			(point))))
315	        0)
316	
317	       ((parent-is "^data_constructors$") parent 0)
318	
319	       ;; where
320	       ((lambda (node _ _)
321	          (let ((n (treesit-node-prev-sibling node)))
322	            (while (string= "comment" (treesit-node-type n))
323	              (setq n (treesit-node-prev-sibling n)))
324	            (string= "where" (treesit-node-type n))))
325	
326	        (lambda (_ b _)
327	          (+ 1 (treesit-node-start (treesit-node-prev-sibling b))))
328	        1)
329	       ((parent-is "local_binds\\|instance_declarations") ,p-prev-sib 0)
330	
331	       ;; Match
332	       ((lambda (node _ _)
333	          (and (string= "match" (treesit-node-type node))
334	               (string-match (regexp-opt '("patterns" "variable"))
335	                             (treesit-node-type (funcall ,p-n-prev node)))))
336	        standalone-parent 2)
337	
338	       ((node-is "match") ,p-prev-sib 1)
339	       ((parent-is "match") haskell-ts--stand-alone-parent 2)
340	
341	       ((parent-is "^haskell$") column-0 0)
342	       ((parent-is "^declarations$") column-0 0)
343	
344	       ((parent-is "^record$") standalone-parent 2)
345	
346	       ((parent-is "^exports$")
347	        (lambda (_ b _) (treesit-node-start (treesit-node-prev-sibling b)))
348	        0)
349	       ((n-p-gp nil "signature" "foreign_import") grand-parent 3)
350	       ((parent-is "^case$") parent 2)
351	       ((node-is "^alternatives$")
352	        (lambda (_ b _)
353	          (treesit-node-start (treesit-node-child b 0)))
354	        2)
355	       ((node-is "^comment$")
356	        (lambda (node parent _)
357	          (pcase node
358	            ;; (relevent means type not it haskell-ts--ignore-types)
359	            ;; 1. next relevent sibling if exists
360	            ((app ,p-sib (and (pred (not null)) n))
361	             (treesit-node-start n))
362	            ;; 2. previous relevent sibling if exists
363	            ((app ,p-prev-sib (and (pred (not null)) n))
364	             n)
365	            ;; 3. parent
366	            (_ (treesit-node-start parent))))
367	        0)
368	
369	       ((node-is "|") parent 1)
370	
371	       ;; Signature
372	       ((n-p-gp nil "function" "function\\|signature") parent -3)
373	       
374	       ;; Backup
375	       (catch-all parent 2))))
376	  "\"Simple\" treesit indentation rules for haskell.")
377	
378	(defvar haskell-ts-mode-syntax-table
379	  (eval-when-compile
380	    (let ((table (make-syntax-table))
381	          (syntax-list
382	           `((" " " \t\n\r\f\v")
383	             ("_" "!#$%&*+./<=>?\\^|-~:")
384	             ("w" ?_ ?\')
385	             ("." ",:@")
386	             ("\"" ?\")
387	             ("()" ?\()
388	             (")(" ?\))
389	             ("(]" ?\[)
390	             (")[" ?\])
391	             ("$`" ?\`)
392	             ("(}1nb" ?\{ )
393	             ("){4nb" ?\} )
394	             ("_ 123" ?- )
395	             (">" "\r\n\f\v"))))
396	      (dolist (ls syntax-list table)
397	        (dolist (char (if (stringp (cadr ls))
398	                          (string-to-list (cadr ls))
399	                        (cdr ls)))
400	          (modify-syntax-entry char (car ls) table)))))
401	  "The syntax table for haskell.")
402	
403	(defun haskell-ts-sexp (node)
404	  "Returns non-nil on a sexp node."
405	  (let ((node-text (treesit-node-text node 1)))
406	    (and
407	     (not (member node-text '( "{" "}" "[" "]" "(" ")" ";")))
408	     (not (and (string= "operator" (treesit-node-field-name node))
409	               (= 1 (length node-text)))))))
410	
411	(defvar haskell-ts-thing-settings
412	  `((haskell
413	     (sexp haskell-ts-sexp)
414	     (sentence "match")
415	     (string "string")
416	     (text "string")))
417	  "`treesit-thing-settings' for `haskell-ts-mode'.")
418	
419	(defmacro haskell-ts-imenu-name-function (check-func)
420	  `(lambda (node)
421	     (let ((nn (treesit-node-child node 0 node)))
422	       (if (funcall ,check-func node)
423	           (if (string= (treesit-node-type nn) "infix")
424	               (treesit-node-text (treesit-node-child nn 1))
425	             (haskell-ts-defun-name node))
426	         nil))))
427	
428	(defvar-keymap  haskell-ts-mode-map
429	  :doc "Keymap for haskell-ts-mode."
430	  "C-c C-c" #'haskell-ts-compile-region-and-go
431	  "C-c C-r" #'run-haskell)
432	
433	;;;###autoload
434	(define-derived-mode haskell-ts-mode prog-mode "haskell ts mode"
435	  "Major mode for Haskell files using tree-sitter."
436	  :table haskell-ts-mode-syntax-table
437	  (unless (treesit-ready-p 'haskell)
438	    (error "Tree-sitter for Haskell is not available"))
439	  (setq treesit-primary-parser (treesit-parser-create 'haskell))
440	  (setq treesit-language-at-point-function
441	        (lambda (&rest _) 'haskell))
442	  (setq-local treesit-defun-type-regexp "\\(?:\\(?:function\\|struct\\)_definition\\)")
443	  ;; Indent
444	  (when haskell-ts-use-indent
445	    (setq-local treesit-simple-indent-rules haskell-ts-indent-rules)
446	    (setq-local indent-tabs-mode nil))
447	  (setq-local electric-indent-functions '(haskell-ts-indent-after-newline))
448	  ;; Comment
449	  (setq-local comment-start "-- ")
450	  (setq-local comment-use-syntax t)
451	  (setq-local comment-start-skip "\\(?: \\|^\\)--+")
452	  ;; Electric
453	  (setq-local electric-pair-pairs
454	              '((?` . ?`) (?\( . ?\)) (?{ . ?}) (?\" . ?\") (?\[ . ?\])))
455	  ;; Navigation
456	  (setq-local treesit-defun-name-function 'haskell-ts-defun-name)
457	  (setq-local treesit-thing-settings haskell-ts-thing-settings)
458	  (setq-local treesit-defun-type-regexp
459	              ;; Since haskell is strict functional, any 2nd level
460	              ;; entity is defintion
461	              (cons ".+"
462	                    (lambda (node)
463	                      (and (not (string-match haskell-ts--ignore-types (treesit-node-type node)))
464	                           (string= "declarations" (treesit-node-type (treesit-node-parent node)))))))
465	  (setq-local prettify-symbols-alist
466	              (append haskell-ts-prettify-symbols-alist
467	                      (and haskell-ts-prettify-words
468	                           haskell-ts-prettify-words-alist)))
469	
470	  ;; Imenu
471	  (setq-local treesit-simple-imenu-settings
472	              `((nil haskell-ts-imenu-func-node-p nil
473	                     ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-func-node-p))
474	                ("Signatures.." haskell-ts-imenu-sig-node-p nil
475	                 ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-sig-node-p))
476	                ("Data..." haskell-ts-imenu-data-type-p nil
477	                 (lambda (node)
478	                   (treesit-node-text (treesit-node-child node 1))))))
479	  ;; font-lock
480	  (setq-local treesit-font-lock-level haskell-ts-font-lock-level)
481	  (setq-local treesit-font-lock-settings haskell-ts-font-lock)
482	  (setq-local treesit-font-lock-feature-list
483	              haskell-ts-font-lock-feature-list)
484	  (treesit-major-mode-setup))
485	
486	(defun haskell-ts-indent-after-newline (c)
487	  (when (eq c ?\n)
488	      (let ((previous-line-width
489		     (save-excursion
490		       (goto-char (line-end-position 0))
491		       (current-column))))
492		(insert (make-string previous-line-width ?\s))))
493	  nil)
494	
495	(defun haskell-ts--fontify-func (node face)
496	  (if (string= "variable" (treesit-node-type node))
497	      (put-text-property
498	       (treesit-node-start node)
499	       (treesit-node-end node)
500	       'face face)
501	    (mapc (lambda (n) (haskell-ts--fontify-func n face))
502	          (treesit-node-children node))))
503	
504	(defun haskell-ts--fontify-arg (node &optional _ _ _)
505	  (haskell-ts--fontify-func node 'font-lock-variable-name-face))
506	
507	(defun haskell-ts--fontify-params (node &optional _ _ _)
508	  (haskell-ts--fontify-func node 'font-lock-function-name-face))
509	
510	(defun haskell-ts--fontify-type (node &optional _ _ _)
511	  (let ((last-child (treesit-node-child node -1)))
512	    (if (string= (treesit-node-type last-child) "function")
513	        (haskell-ts--fontify-type last-child)
514	      (put-text-property
515	       (treesit-node-start last-child)
516	       (treesit-node-end last-child)
517	       'face 'font-lock-variable-name-face))))
518	
519	(defun haskell-ts-imenu-node-p (regex node)
520	  (and (string-match-p regex (treesit-node-type node))
521	       (string= (treesit-node-type (treesit-node-parent node)) "declarations")))
522	
523	(defun haskell-ts-imenu-func-node-p (node)
524	  (haskell-ts-imenu-node-p "function\\|bind" node))
525	
526	(defun haskell-ts-imenu-sig-node-p (node)
527	  (haskell-ts-imenu-node-p "signature" node))
528	
529	(defun haskell-ts-imenu-data-type-p (node)
530	  (haskell-ts-imenu-node-p "data_type" node))
531	
532	(defun haskell-ts-defun-name (node)
533	  (treesit-node-text (treesit-node-child node 0)))
534	
535	(defun haskell-ts-compile-region-and-go ()
536	  "Compile the text from START to END in the haskell proc.
537	If region is not active, reload the whole file."
538	  (interactive)
539	  (let ((hs (haskell-ts-haskell-session)))
540	    (if (region-active-p)
541	        (let ((str (buffer-substring-no-properties
542	                    (region-beginning)
543	                    (region-end))))
544	          (comint-send-string hs ":{\n")
545	          (comint-send-string
546	           hs
547	           ;; Things that may cause problem to ghci need to be
548	           ;; escaped.  TODO examine if other lines that start with
549	           ;; colons might cause problems
550	           (replace-regexp-in-string "^:\\}" "\\:}" str nil t))
551	          (comint-send-string hs "\n:}\n"))
552	      (comint-send-string hs ":r\n"))))
553	
554	;;;###autoload
555	(defun run-haskell ()
556	  "Run an inferior Haskell process."
557	  (interactive)
558	  (let ((buffer (concat "*" haskell-ts-ghci-buffer-name "*")))
559	    (pop-to-buffer-same-window
560	     (if (comint-check-proc buffer)
561	         buffer
562	       (make-comint haskell-ts-ghci-buffer-name haskell-ts-ghci nil buffer-file-name)))))
563	
564	(defun haskell-ts-haskell-session ()
565	  (get-buffer-process (concat "*" haskell-ts-ghci-buffer-name "*")))
566	
567	(when (treesit-ready-p 'haskell)
568	  (add-to-list 'auto-mode-alist '("\\.hs\\'" . haskell-ts-mode)))
569	
570	(provide 'haskell-ts-mode)
571	
572	;; derive from `haskell-mode' on emacs v30+
573	(when (functionp 'derived-mode-add-parents)
574	  (derived-mode-add-parents 'haskell-ts-mode '(haskell-mode)))
575	
576	;;; haskell-ts-mode.el ends here