From unknown Sat Jun 21 03:07:49 2025 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Mailer: MIME-tools 5.509 (Entity 5.509) Content-Type: text/plain; charset=utf-8 From: bug#75184 <75184@debbugs.gnu.org> To: bug#75184 <75184@debbugs.gnu.org> Subject: Status: Merge haskell-ts-mode to master Reply-To: bug#75184 <75184@debbugs.gnu.org> Date: Sat, 21 Jun 2025 10:07:49 +0000 retitle 75184 Merge haskell-ts-mode to master reassign 75184 emacs submitter 75184 Pranshu Sharma severity 75184 wishlist tag 75184 patch thanks From debbugs-submit-bounces@debbugs.gnu.org Sun Dec 29 12:14:19 2024 Received: (at submit) by debbugs.gnu.org; 29 Dec 2024 17:14:19 +0000 Received: from localhost ([127.0.0.1]:55993 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tRwrq-0002Yl-EJ for submit@debbugs.gnu.org; Sun, 29 Dec 2024 12:14:19 -0500 Received: from lists.gnu.org ([209.51.188.17]:47698) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tRv8K-0005fi-QW for submit@debbugs.gnu.org; Sun, 29 Dec 2024 10:23:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tRv8J-0002mE-P5 for bug-gnu-emacs@gnu.org; Sun, 29 Dec 2024 10:23:12 -0500 Received: from mail.bauherren.ovh ([45.32.179.127]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tRv8H-0000jX-Kk for bug-gnu-emacs@gnu.org; Sun, 29 Dec 2024 10:23:11 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=bauherren.ovh; s=mail; t=1735485784; bh=PLUOAHYPaIbW8SKFisNcicyhuc8vkR5odLxePzKXDh8=; h=From:To:Subject:Date:From; b=MkjmctqtwcKJsoWYUeikZWnHXBa7v2QRhkli4w4UcD3Qw8TWlRMaOXcLuJDwyIlEQ Lt1xveX8J6RZwaa1qVPFd8Nfp3myCVrzhBJ6DVp27sszt160uc7XqTErWOCDCB5V2B aXWkqiQu0H9zCDTEQSpPmmoqUDoG9o6prIJ/jBRxcNXaMQpN6xDt2dVf7RtSOsIw4u Br7KKbHaH1IBa2QiQp1d4hVknleqpL1UQxi0+/1fy/5B9+rcH3c2BRAI3oThMTHSH2 3S+PFVxwyXYQyhSbAgu4XMtIjSOV1N3cdvsbeHzcd8MW4PmoKiWf2znFsszePlpIvx bLyyWCfcnhgFQ== From: Pranshu Sharma To: bug-gnu-emacs@gnu.org Subject: Merge haskell-ts-mode to master Date: Mon, 30 Dec 2024 01:22:54 +1000 Message-ID: <87v7v27bu9.fsf@bauherren.ovh> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=45.32.179.127; envelope-from=pranshu@bauherren.ovh; helo=mail.bauherren.ovh X-Spam_score_int: 5 X-Spam_score: 0.5 X-Spam_bar: / X-Spam_report: (0.5 / 5.0 requ) BAYES_05=-0.5, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, FROM_FMBLA_NEWDOM28=0.798, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Spam-Score: -0.6 (/) X-Debbugs-Envelope-To: submit X-Mailman-Approved-At: Sun, 29 Dec 2024 12:14:10 -0500 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.6 (-) --=-=-= Content-Type: text/plain Hello all, This is request to merge haskell-ts-mode to master. The code is here https://codeberg.org/pranshu/haskell-ts-mode , but I also attacched file. I suggest you look in codeberg repo for latest changes. Phillip kauldarlic has done a review of the non-treesit specific part of the code in emacs-devel, *I need someone* to review treesit specific parts of it now. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=haskell-ts-mode.el Content-Transfer-Encoding: quoted-printable ;;; haskell-ts-mode.el --- A treesit based major mode for haskell -*- lexic= al-binding:t -*- ;; Copyright (C) 2024 Pranshu Sharma ;; Author: Pranshu Sharma ;; URL: https://codeberg.org/pranshu/haskell-ts-mode ;; Package-Requires: ((emacs "29.3")) ;; Version: 1 ;; Keywords: languages, haskell ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; This is a major mode that uses treesitter to provide all the basic ;; major mode stuff, like indentation, font lock, etc... ;; It uses the grammer at: https://github.com/tree-sitter/tree-sitter-haske= ll ;;; Code: (require 'comint) (require 'treesit) (declare-function treesit-parser-create "treesit.c") (declare-function treesit-node-start "treesit.c") (declare-function treesit-node-parent "treesit.c") (declare-function treesit-node-prev-sibling "treesit.c") (declare-function treesit-node-next-sibling "treesit.c") (declare-function treesit-node-end "treesit.c") (declare-function treesit-node-child "treesit.c") (declare-function treesit-node-type "treesit.c") (defgroup haskell-ts-mode nil "Group that contains haskell-ts-mode variables" :group 'langs) (defvar haskell-ts-font-lock-feature-list `((comment str pragma parens) (type definition function args) (match keyword) (otherwise signature type-sig))) (defcustom haskell-ts-ghci "ghci" "The command to be called to run ghci." :type 'string) (defcustom haskell-ts-ghci-buffer-name "Inferior Haskell" "Buffer name for the ghci prcoess." :type 'string) (defcustom haskell-ts-use-indent t "Set to non-nil to use the indentation provided by haskell-ts-mode" :type 'boolean) (defcustom haskell-ts-font-lock-level 4 "Level of font lock, 1 for minimum highlghting and 4 for maximum." :type '(choice (const :tag "Minimal Highlighting" 1) (const :tag "Low Highlighting" 2) (const :tag "High Highlighting" 3) (const :tag "Maximum Highlighting" 4))) (defvar haskell-ts-prettify-symbols-alist '(("\\" . "=CE=BB") ("/=3D" . "=E2=89=A0") ("->" . "=E2=86=92") ("=3D>" . "=E2=87=92") ("<-" . "=E2=86=90") ("<=3D" . "=E2=89=A5") (">=3D" . "=E2=89=A4"))) (defvar haskell-ts-font-lock (treesit-font-lock-rules :language 'haskell :feature 'keyword `(["module" "import" "data" "let" "where" "case" "type" "if" "then" "else" "of" "do" "in" "instance" "class"] @font-lock-keyword-face) :language 'haskell :feature 'otherwise :override t `(((match (guards guard: (boolean (variable) @font-lock-keyword-face))) (:match "otherwise" @font-lock-keyword-face))) :language 'haskell :feature 'type-sig "(signature (binding_list (variable) @font-lock-doc-markup-face)) (signature (variable) @font-lock-doc-markup-face)" :language 'haskell :feature 'args :override 'keep (concat "(function (infix left_operand: (_) @haskell-ts--fontify-arg))" "(function (infix right_operand: (_) @haskell-ts--fontify-arg))" "(generator . (_) @haskell-ts--fontify-arg)" "(bind (as (variable) . (_) @haskell-ts--fontify-arg))" "(patterns) @haskell-ts--fontify-arg") :language 'haskell :feature 'type `((type) @font-lock-type-face (constructor) @font-lock-type-face) :language 'haskell :override t :feature 'signature `((signature (function) @haskell-ts--fontify-type) (context (function) @haskell-ts--fontify-type)) :language 'haskell :feature 'match `((match ("|" @font-lock-doc-face) ("=3D" @font-lock-doc-face)) (list_comprehension ("|" @font-lock-doc-face (qualifiers (generator "<-" @font-lock-doc-face)))) (match ("->" @font-lock-doc-face))) :language 'haskell :feature 'comment `(((comment) @font-lock-comment-face) ((haddock) @font-lock-doc-face)) :language 'haskell :feature 'pragma `((pragma) @font-lock-preprocessor-face (cpp) @font-lock-preprocessor-face) :language 'haskell :feature 'str :override t `((char) @font-lock-string-face (string) @font-lock-string-face (quasiquote (quoter) @font-lock-type-face) (quasiquote (quasiquote_body) @font-lock-preprocessor-face)) :language 'haskell :feature 'parens :override t `(["(" ")" "[" "]"] @font-lock-operator-face (infix operator: (_) @font-lock-operator-face)) :language 'haskell :feature 'function :override t `((function name: (variable) @font-lock-function-name-face) (function (infix (operator) @font-lock-function-name-face)) (declarations (type_synomym (name) @font-lock-function-name-face)) (bind (variable) @font-lock-function-name-face) (function (infix (infix_id (variable) @font-lock-function-name-face))) (bind (as (variable) @font-lock-function-name-face)))) "The treesitter font lock settings for haskell.") (defun haskell-ts--stand-alone-parent (_ parent bol) (save-excursion (goto-char (treesit-node-start parent)) (let ((type (treesit-node-type parent))) (if (and (not bol) (or (looking-back "^[ \t]*" (line-beginning-position)) (member type '("when" "where" "do" "let" "local_binds" "function")))) (treesit-node-start parent) (haskell-ts--stand-alone-parent 1 (funcall (if bol 'treesit-node-parent 'identity) (treesit-node-parent parent)) nil))))) (defvar haskell-ts--ignore-types (regexp-opt '("comment" "cpp" "haddock")) "Node types that will be ignored by indentation.") (defvar haskell-ts-indent-rules (let* ((p-sib (lambda (node &optional arg) (let* ((func (if arg #'treesit-node-prev-sibling #'treesit-node-next-sibling)) (n (funcall func node))) (while (and n (string-match haskell-ts--ignore-types (treesit-node-type n))) (setq n (funcall func n))) n))) (p-prev-sib (lambda (node &optional _ _) (treesit-node-start (funcall p-sib node t))= )) (p-n-prev (lambda (node) (funcall p-sib node t))) (parent-first-child (lambda (_ parent _) (treesit-node-start (treesit-node-child parent 0))))) `((haskell ((node-is "^cpp$") column-0 0) ((parent-is "^comment$") column-0 0) ((parent-is "^haddock$") column-0 0) ((parent-is "^imports$") column-0 0) ;; Infix ((n-p-gp nil "infix" "infix") (lambda (_ node _) (let ((first-inf nil)) (while (string=3D "infix" (treesit-node-type (setq node (treesit-node-parent node)))) (setq first-inf node)) (funcall ,parent-first-child nil first-inf nil))) 0) ((node-is "^infix$") ,parent-first-child 0) ;; Lambda ((parent-is "^lambda\\(_case\\)?$") standalone-parent 2) ((parent-is "^class_declarations$") prev-sibling 0) ((node-is "^where$") parent 2) ;; in ((node-is "^in$") parent 0) ((parent-is "qualifiers") parent 0) ;; list ((node-is "^]$") parent 0) ((parent-is "^list$") standalone-parent 2) ;; If then else ((node-is "^then$") parent 2) ((node-is "^else$") parent 2) ((parent-is "^apply$") haskell-ts--stand-alone-parent 1) ((node-is "^quasiquote$") grand-parent 2) ((parent-is "^quasiquote_body$") (lambda (_ _ c) c) 0) ((lambda (node parent bol) (when-let ((n (treesit-node-prev-sibling node))) (while (string=3D "comment" (treesit-node-type n)) (setq n (treesit-node-prev-sibling n))) (string=3D "do" (treesit-node-type n)))) haskell-ts--stand-alone-parent 3) ((parent-is "^do$") ,p-prev-sib 0) ((parent-is "^alternatives$") ,p-prev-sib 0) ;; prev-adaptive-prefix is broken sometimes (no-node (lambda (_ _ _) (save-excursion (goto-char (line-beginning-position 0)) (back-to-indentation) (point))) 0) ((parent-is "^data_constructors$") parent 0) ;; where ((lambda (node _ _) (let ((n (treesit-node-prev-sibling node))) (while (string=3D "comment" (treesit-node-type n)) (setq n (treesit-node-prev-sibling n))) (string=3D "where" (treesit-node-type n)))) (lambda (_ b _) (+ 1 (treesit-node-start (treesit-node-prev-sibling b)))) 3) ((parent-is "local_binds\\|instance_declarations") ,p-prev-sib 0) ;; Match ((lambda (node _ _) (and (string=3D "match" (treesit-node-type node)) (string-match (regexp-opt '("patterns" "variable")) (treesit-node-type (funcall ,p-n-prev node))))) standalone-parent 2) ((node-is "match") ,p-prev-sib 0) ((parent-is "match") standalone-parent 2) ((parent-is "^haskell$") column-0 0) ((parent-is "^declarations$") column-0 0) ((parent-is "^record$") standalone-parent 2) ((parent-is "^exports$") (lambda (_ b _) (treesit-node-start (treesit-node-prev-sibling b))) 0) ((n-p-gp nil "signature" "foreign_import") grand-parent 3) ((parent-is "^case$") standalone-parent 4) ((node-is "^alternatives$") (lambda (_ b _) (treesit-node-start (treesit-node-child b 0))) 2) ((node-is "^comment$") (lambda (node parent _) (pcase node ;; (relevent means type not it haskell-ts--ignore-types) ;; 1. next relevent sibling if exists ((app ,p-sib (and (pred (not null)) n)) (treesit-node-start n)) ;; 2. previous relevent sibling if exists ((app ,p-prev-sib (and (pred (not null)) n)) n) ;; 3. parent (_ (treesit-node-start parent)))) 0) ;; Backup (catch-all parent 2)))) "\"Simple\" treesit indentation rules for haskell.") ;; Copied from haskell-tng-mode, changed a bit (defvar haskell-ts-mode-syntax-table (eval-when-compile (let ((table (make-syntax-table)) (syntax-list '(("_" ?! ?_) ("w" ?') ;; Haskell has some goofy comment enders like C-q C-l (">" 13 10 12 11) ("_ 123" ?-) ("(}1nb" ?\{) ("){4nb" ?\}) ("<" ?#) (">" ?\n) ;; Special operaters ("." ?\, ?\; ?@) ("\"" ?\") ("$`" ?\`)))) ;; The defaults are mostly fine (dolist (ls syntax-list table) (dolist (char (cdr ls)) (modify-syntax-entry char (car ls) table)))))) (defmacro haskell-ts-imenu-name-function (check-func) `(lambda (node) (let ((nn (treesit-node-child node 0 node))) (if (funcall ,check-func node) (if (string=3D (treesit-node-type nn) "infix") (treesit-node-text (treesit-node-child nn 1)) (haskell-ts-defun-name node)) nil)))) (defvar-keymap haskell-ts-mode-map :doc "Keymap for haskell-ts-mode." "C-c C-c" 'haskell-ts-compile-region-and-go "C-c C-r" 'run-haskell 'haskell-ts-indent-defun) ;;;###autoload (define-derived-mode haskell-ts-mode prog-mode "haskell ts mode" "Major mode for Haskell files using tree-sitter." (unless (treesit-ready-p 'haskell) (error "Tree-sitter for Haskell is not available")) (treesit-parser-create 'haskell) (setq-local treesit-defun-type-regexp "\\(?:\\(?:function\\|struct\\)_def= inition\\)") ;; Indent (when haskell-ts-use-indent (setq-local treesit-simple-indent-rules haskell-ts-indent-rules) (setq-local indent-tabs-mode nil)) ;; Comment (setq-local comment-start "-- ") (setq-local comment-use-syntax t) (setq-local comment-start-skip "\\(?: \\|^\\)-+") ;; Electric (setq-local electric-pair-pairs '((?` . ?`) (?\( . ?\)) (?{ . ?}) (?\" . ?\") (?\[ . ?\]))) ;; Navigation (setq-local treesit-defun-name-function 'haskell-ts-defun-name) (setq-local treesit-defun-type-regexp ;; Since haskell is strict functional, any 2nd level ;; entity is defintion (cons ".+" (lambda (node) (and (not (string-match haskell-ts--ignore-types (treesit-node-type= node))) (string=3D "declarations" (treesit-node-type (treesit-node-parent nod= e))))))) (setq-local prettify-symbols-alist haskell-ts-prettify-symbols-alist) ;; Imenu (setq-local treesit-simple-imenu-settings `((nil haskell-ts-imenu-func-node-p nil ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-func-node-p)) ("Signatures.." haskell-ts-imenu-sig-node-p nil ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-sig-node-p)) ("Data..." haskell-ts-imenu-data-type-p nil (lambda (node) (treesit-node-text (treesit-node-child node 1)))))) ;; font-lock (setq-local treesit-font-lock-level haskell-ts-font-lock-level) (setq-local treesit-font-lock-settings haskell-ts-font-lock) (setq-local treesit-font-lock-feature-list haskell-ts-font-lock-feature-list) (treesit-major-mode-setup)) (defun haskell-ts--fontify-arg (node &optional _ _ _) (if (string=3D "variable" (treesit-node-type node)) (put-text-property (treesit-node-start node) (treesit-node-end node) 'face font-lock-variable-name-face) (mapc 'haskell-ts--fontify-arg (treesit-node-children node)))) (defun haskell-ts--fontify-type (node &optional _ _ _) (let ((last-child (treesit-node-child node -1))) (if (string=3D (treesit-node-type last-child) "function") (haskell-ts--fontify-type last-child) (put-text-property (treesit-node-start last-child) (treesit-node-end last-child) 'face font-lock-variable-name-face)))) (defun haskell-ts-imenu-node-p (regex node) (and (string-match-p regex (treesit-node-type node)) (string=3D (treesit-node-type (treesit-node-parent node)) "declarati= ons"))) (defun haskell-ts-imenu-func-node-p (node) (haskell-ts-imenu-node-p "function\\|bind" node)) (defun haskell-ts-imenu-sig-node-p (node) (haskell-ts-imenu-node-p "signature" node)) (defun haskell-ts-imenu-data-type-p (node) (haskell-ts-imenu-node-p "data_type" node)) (defun haskell-ts-defun-name (node) (treesit-node-text (treesit-node-child node 0))) (defun haskell-ts-compile-region-and-go (start end) "Compile the text from START to END in the haskell proc." (interactive "r") (let ((hs (haskell-ts-haskell-session)) (str (buffer-substring-no-properties start end))) (comint-send-string hs ":{\n") (comint-send-string hs (replace-regexp-in-string "^:\\}" "\\:}" str nil t)) (comint-send-string hs "\n:}\n"))) (defun run-haskell () (interactive) (pop-to-buffer-same-window (if (comint-check-proc (concat "*" haskell-ts-ghci-buffer-name "*")) haskell-ts-ghci-buffer-name (make-comint haskell-ts-ghci-buffer-name haskell-ts-ghci nil buffer-fi= le-name)))) (defun haskell-ts-haskell-session () (get-buffer-process (concat "*" haskell-ts-ghci-buffer-name "*"))) (when (treesit-ready-p 'haskell) (add-to-list 'auto-mode-alist '("\\.hs\\'" . haskell-ts-mode))) (provide 'haskell-ts-mode) ;;; haskell-ts-mode.el ends here --=-=-= Content-Type: text/plain -- Pranshu Sharma --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Mon Dec 30 05:53:57 2024 Received: (at 75184) by debbugs.gnu.org; 30 Dec 2024 10:53:57 +0000 Received: from localhost ([127.0.0.1]:57467 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tSDPJ-00020J-1o for submit@debbugs.gnu.org; Mon, 30 Dec 2024 05:53:57 -0500 Received: from mail.bauherren.ovh ([45.32.179.127]:58354) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tSDPH-000205-Ga for 75184@debbugs.gnu.org; Mon, 30 Dec 2024 05:53:56 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=bauherren.ovh; s=mail; t=1735555998; bh=8ngldf5aXSKb6+/RQEqfjUuo4ovnzmZ4ZpwSlxChACc=; h=From:To:Subject:Date:From; b=YgzQv7Kh5OK8krnfyiLIRxe8+LrPugqyNuT1bDNa+Rujfs9Gyv9mY3/evg94uu7IA sZj3dANctpL7iVxIdDIAdAMzrg8QNY1VJf6GmB/UTyZ0rCyyAq07SllRQQUVNM89ea wW0aH2OK3JCESuRFoCvRRbk2/xnvWppTIoUmpyAgnW/lQlVshVtVhb1xWhsZtVV3U6 qzVcLfrEJezqQ3DLHgyFELOz/jIPdC220JW/y7D9GdEnPqVIw7mRSarwolCLo0vROh O78YAP2UjKXkntVgl1WqufhwsBr6RQPopuiGKYtOslOpOmxnwyV8i0L2HWUYJ8PxBm DBizi2fwn4gug== From: Pranshu Sharma To: 75184@debbugs.gnu.org Subject: URGENT !! Date: Mon, 30 Dec 2024 20:53:06 +1000 Message-ID: <87jzbh788d.fsf@bauherren.ovh> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 0.8 (/) X-Debbugs-Envelope-To: 75184 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.2 (/) --=-=-= Content-Type: text/plain The version sent in my last email has a major bug, new version is on updaate codeberg repo and attached. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=haskell-ts-mode.el Content-Transfer-Encoding: quoted-printable ;;; haskell-ts-mode.el --- A treesit based major mode for haskell -*- lexic= al-binding:t -*- ;; Copyright (C) 2024 Pranshu Sharma ;; Author: Pranshu Sharma ;; URL: https://codeberg.org/pranshu/haskell-ts-mode ;; Package-Requires: ((emacs "29.3")) ;; Version: 1 ;; Keywords: languages, haskell ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; This is a major mode that uses treesitter to provide all the basic ;; major mode stuff, like indentation, font lock, etc... ;; It uses the grammer at: https://github.com/tree-sitter/tree-sitter-haske= ll ;;; Code: (require 'comint) (require 'treesit) (declare-function treesit-parser-create "treesit.c") (declare-function treesit-node-start "treesit.c") (declare-function treesit-node-parent "treesit.c") (declare-function treesit-node-prev-sibling "treesit.c") (declare-function treesit-node-next-sibling "treesit.c") (declare-function treesit-node-end "treesit.c") (declare-function treesit-node-child "treesit.c") (declare-function treesit-node-type "treesit.c") (defgroup haskell-ts-mode nil "Group that contains haskell-ts-mode variables" :group 'langs) (defvar haskell-ts-font-lock-feature-list `((comment str pragma parens) (type definition function args) (match keyword) (otherwise signature type-sig))) (defcustom haskell-ts-ghci "ghci" "The command to be called to run ghci." :type 'string) (defcustom haskell-ts-ghci-buffer-name "Inferior Haskell" "Buffer name for the ghci prcoess." :type 'string) (defcustom haskell-ts-use-indent t "Set to non-nil to use the indentation provided by haskell-ts-mode" :type 'boolean) (defcustom haskell-ts-font-lock-level 4 "Level of font lock, 1 for minimum highlghting and 4 for maximum." :type '(choice (const :tag "Minimal Highlighting" 1) (const :tag "Low Highlighting" 2) (const :tag "High Highlighting" 3) (const :tag "Maximum Highlighting" 4))) (defvar haskell-ts-prettify-symbols-alist '(("\\" . "=CE=BB") ("/=3D" . "=E2=89=A0") ("->" . "=E2=86=92") ("=3D>" . "=E2=87=92") ("<-" . "=E2=86=90") ("<=3D" . "=E2=89=A5") (">=3D" . "=E2=89=A4"))) (defvar haskell-ts-font-lock (treesit-font-lock-rules :language 'haskell :feature 'keyword `(["module" "import" "data" "let" "where" "case" "type" "if" "then" "else" "of" "do" "in" "instance" "class"] @font-lock-keyword-face) :language 'haskell :feature 'otherwise :override t `(((match (guards guard: (boolean (variable) @font-lock-keyword-face))) (:match "otherwise" @font-lock-keyword-face))) :language 'haskell :feature 'type-sig "(signature (binding_list (variable) @font-lock-doc-markup-face)) (signature (variable) @font-lock-doc-markup-face)" :language 'haskell :feature 'args :override 'keep (concat "(function (infix left_operand: (_) @haskell-ts--fontify-arg))" "(function (infix right_operand: (_) @haskell-ts--fontify-arg))" "(generator . (_) @haskell-ts--fontify-arg)" "(bind (as (variable) . (_) @haskell-ts--fontify-arg))" "(patterns) @haskell-ts--fontify-arg") :language 'haskell :feature 'type `((type) @font-lock-type-face (constructor) @font-lock-type-face) :language 'haskell :override t :feature 'signature `((signature (function) @haskell-ts--fontify-type) (context (function) @haskell-ts--fontify-type)) :language 'haskell :feature 'match `((match ("|" @font-lock-doc-face) ("=3D" @font-lock-doc-face)) (list_comprehension ("|" @font-lock-doc-face (qualifiers (generator "<-" @font-lock-doc-face)))) (match ("->" @font-lock-doc-face))) :language 'haskell :feature 'comment `(((comment) @font-lock-comment-face) ((haddock) @font-lock-doc-face)) :language 'haskell :feature 'pragma `((pragma) @font-lock-preprocessor-face (cpp) @font-lock-preprocessor-face) :language 'haskell :feature 'str :override t `((char) @font-lock-string-face (string) @font-lock-string-face (quasiquote (quoter) @font-lock-type-face) (quasiquote (quasiquote_body) @font-lock-preprocessor-face)) :language 'haskell :feature 'parens :override t `(["(" ")" "[" "]"] @font-lock-operator-face (infix operator: (_) @font-lock-operator-face)) :language 'haskell :feature 'function :override t `((function name: (variable) @font-lock-function-name-face) (function (infix (operator) @font-lock-function-name-face)) (declarations (type_synomym (name) @font-lock-function-name-face)) (bind (variable) @font-lock-function-name-face) (function (infix (infix_id (variable) @font-lock-function-name-face))) (bind (as (variable) @font-lock-function-name-face)))) "The treesitter font lock settings for haskell.") (defun haskell-ts--stand-alone-parent (_ parent bol) (save-excursion (goto-char (treesit-node-start parent)) (let ((type (treesit-node-type parent))) (if (and (not bol) (or (looking-back "^[ \t]*" (line-beginning-position)) (member type '("when" "where" "do" "let" "local_binds" "function")))) (treesit-node-start parent) (haskell-ts--stand-alone-parent 1 (funcall (if bol 'treesit-node-parent 'identity) (treesit-node-parent parent)) nil))))) (defvar haskell-ts--ignore-types (regexp-opt '("comment" "cpp" "haddock")) "Node types that will be ignored by indentation.") (defvar haskell-ts-indent-rules (let* ((p-sib (lambda (node &optional arg) (let* ((func (if arg #'treesit-node-prev-sibling #'treesit-node-next-sibling)) (n (funcall func node))) (while (and n (string-match haskell-ts--ignore-types (treesit-node-type n))) (setq n (funcall func n))) n))) (p-prev-sib (lambda (node &optional _ _) (treesit-node-start (funcall p-sib node t))= )) (p-n-prev (lambda (node) (funcall p-sib node t))) (parent-first-child (lambda (_ parent _) (treesit-node-start (treesit-node-child parent 0))))) `((haskell ((node-is "^cpp$") column-0 0) ((parent-is "^comment$") column-0 0) ((parent-is "^haddock$") column-0 0) ((parent-is "^imports$") column-0 0) ;; Infix ((n-p-gp nil "infix" "infix") (lambda (_ node _) (let ((first-inf nil)) (while (string=3D "infix" (treesit-node-type (setq node (treesit-node-parent node)))) (setq first-inf node)) (funcall ,parent-first-child nil first-inf nil))) 0) ((node-is "^infix$") ,parent-first-child 0) ;; Lambda ((parent-is "^lambda\\(_case\\)?$") standalone-parent 2) ((parent-is "^class_declarations$") prev-sibling 0) ((node-is "^where$") parent 2) ;; in ((node-is "^in$") parent 0) ((parent-is "qualifiers") parent 0) ;; list ((node-is "^]$") parent 0) ((parent-is "^list$") standalone-parent 2) ;; If then else ((node-is "^then$") parent 2) ((node-is "^else$") parent 2) ((parent-is "^apply$") haskell-ts--stand-alone-parent 1) ((node-is "^quasiquote$") grand-parent 2) ((parent-is "^quasiquote_body$") (lambda (_ _ c) c) 0) ((lambda (node parent bol) (when-let ((n (treesit-node-prev-sibling node))) (while (string=3D "comment" (treesit-node-type n)) (setq n (treesit-node-prev-sibling n))) (string=3D "do" (treesit-node-type n)))) haskell-ts--stand-alone-parent 3) ((parent-is "^do$") ,p-prev-sib 0) ((parent-is "^alternatives$") ,p-prev-sib 0) ;; prev-adaptive-prefix is broken sometimes (no-node (lambda (_ _ _) (save-excursion (goto-char (line-beginning-position 0)) (back-to-indentation) (point))) 0) ((parent-is "^data_constructors$") parent 0) ;; where ((lambda (node _ _) (let ((n (treesit-node-prev-sibling node))) (while (string=3D "comment" (treesit-node-type n)) (setq n (treesit-node-prev-sibling n))) (string=3D "where" (treesit-node-type n)))) (lambda (_ b _) (+ 1 (treesit-node-start (treesit-node-prev-sibling b)))) 3) ((parent-is "local_binds\\|instance_declarations") ,p-prev-sib 0) ;; Match ((lambda (node _ _) (and (string=3D "match" (treesit-node-type node)) (string-match (regexp-opt '("patterns" "variable")) (treesit-node-type (funcall ,p-n-prev node))))) standalone-parent 2) ((node-is "match") ,p-prev-sib 0) ((parent-is "match") standalone-parent 2) ((parent-is "^haskell$") column-0 0) ((parent-is "^declarations$") column-0 0) ((parent-is "^record$") standalone-parent 2) ((parent-is "^exports$") (lambda (_ b _) (treesit-node-start (treesit-node-prev-sibling b))) 0) ((n-p-gp nil "signature" "foreign_import") grand-parent 3) ((parent-is "^case$") standalone-parent 4) ((node-is "^alternatives$") (lambda (_ b _) (treesit-node-start (treesit-node-child b 0))) 2) ((node-is "^comment$") (lambda (node parent _) (pcase node ;; (relevent means type not it haskell-ts--ignore-types) ;; 1. next relevent sibling if exists ((app ,p-sib (and (pred (not null)) n)) (treesit-node-start n)) ;; 2. previous relevent sibling if exists ((app ,p-prev-sib (and (pred (not null)) n)) n) ;; 3. parent (_ (treesit-node-start parent)))) 0) ;; Backup (catch-all parent 2)))) "\"Simple\" treesit indentation rules for haskell.") ;; Copied from haskell-tng-mode, changed a bit (defvar haskell-ts-mode-syntax-table (eval-when-compile (let ((table (make-syntax-table)) (syntax-list '(("_" ?! ?_) ("w" ?') ;; Haskell has some goofy comment enders like C-q C-l (">" 13 10 12 11) ("_ 123" ?-) ("(}1nb" ?\{) ("){4nb" ?\}) ("<" ?#) (">" ?\n) ;; Special operaters ("." ?\, ?\; ?@) ("\"" ?\") ("$`" ?\`)))) ;; The defaults are mostly fine (dolist (ls syntax-list table) (dolist (char (cdr ls)) (modify-syntax-entry char (car ls) table)))))) (defmacro haskell-ts-imenu-name-function (check-func) `(lambda (node) (let ((nn (treesit-node-child node 0 node))) (if (funcall ,check-func node) (if (string=3D (treesit-node-type nn) "infix") (treesit-node-text (treesit-node-child nn 1)) (haskell-ts-defun-name node)) nil)))) (defvar-keymap haskell-ts-mode-map :doc "Keymap for haskell-ts-mode." "C-c C-c" 'haskell-ts-compile-region-and-go "C-c C-r" 'run-haskell) ;;;###autoload (define-derived-mode haskell-ts-mode prog-mode "haskell ts mode" "Major mode for Haskell files using tree-sitter." (unless (treesit-ready-p 'haskell) (error "Tree-sitter for Haskell is not available")) (treesit-parser-create 'haskell) (setq-local treesit-defun-type-regexp "\\(?:\\(?:function\\|struct\\)_def= inition\\)") ;; Indent (when haskell-ts-use-indent (setq-local treesit-simple-indent-rules haskell-ts-indent-rules) (setq-local indent-tabs-mode nil)) ;; Comment (setq-local comment-start "-- ") (setq-local comment-use-syntax t) (setq-local comment-start-skip "\\(?: \\|^\\)-+") ;; Electric (setq-local electric-pair-pairs '((?` . ?`) (?\( . ?\)) (?{ . ?}) (?\" . ?\") (?\[ . ?\]))) ;; Navigation (setq-local treesit-defun-name-function 'haskell-ts-defun-name) (setq-local treesit-defun-type-regexp ;; Since haskell is strict functional, any 2nd level ;; entity is defintion (cons ".+" (lambda (node) (and (not (string-match haskell-ts--ignore-types (treesit-node-type= node))) (string=3D "declarations" (treesit-node-type (treesit-node-parent nod= e))))))) (setq-local prettify-symbols-alist haskell-ts-prettify-symbols-alist) ;; Imenu (setq-local treesit-simple-imenu-settings `((nil haskell-ts-imenu-func-node-p nil ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-func-node-p)) ("Signatures.." haskell-ts-imenu-sig-node-p nil ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-sig-node-p)) ("Data..." haskell-ts-imenu-data-type-p nil (lambda (node) (treesit-node-text (treesit-node-child node 1)))))) ;; font-lock (setq-local treesit-font-lock-level haskell-ts-font-lock-level) (setq-local treesit-font-lock-settings haskell-ts-font-lock) (setq-local treesit-font-lock-feature-list haskell-ts-font-lock-feature-list) (treesit-major-mode-setup)) (defun haskell-ts--fontify-arg (node &optional _ _ _) (if (string=3D "variable" (treesit-node-type node)) (put-text-property (treesit-node-start node) (treesit-node-end node) 'face font-lock-variable-name-face) (mapc 'haskell-ts--fontify-arg (treesit-node-children node)))) (defun haskell-ts--fontify-type (node &optional _ _ _) (let ((last-child (treesit-node-child node -1))) (if (string=3D (treesit-node-type last-child) "function") (haskell-ts--fontify-type last-child) (put-text-property (treesit-node-start last-child) (treesit-node-end last-child) 'face font-lock-variable-name-face)))) (defun haskell-ts-imenu-node-p (regex node) (and (string-match-p regex (treesit-node-type node)) (string=3D (treesit-node-type (treesit-node-parent node)) "declarati= ons"))) (defun haskell-ts-imenu-func-node-p (node) (haskell-ts-imenu-node-p "function\\|bind" node)) (defun haskell-ts-imenu-sig-node-p (node) (haskell-ts-imenu-node-p "signature" node)) (defun haskell-ts-imenu-data-type-p (node) (haskell-ts-imenu-node-p "data_type" node)) (defun haskell-ts-defun-name (node) (treesit-node-text (treesit-node-child node 0))) (defun haskell-ts-compile-region-and-go (start end) "Compile the text from START to END in the haskell proc." (interactive "r") (let ((hs (haskell-ts-haskell-session)) (str (buffer-substring-no-properties start end))) (comint-send-string hs ":{\n") (comint-send-string hs (replace-regexp-in-string "^:\\}" "\\:}" str nil t)) (comint-send-string hs "\n:}\n"))) (defun run-haskell () (interactive) (pop-to-buffer-same-window (if (comint-check-proc (concat "*" haskell-ts-ghci-buffer-name "*")) haskell-ts-ghci-buffer-name (make-comint haskell-ts-ghci-buffer-name haskell-ts-ghci nil buffer-fi= le-name)))) (defun haskell-ts-haskell-session () (get-buffer-process (concat "*" haskell-ts-ghci-buffer-name "*"))) (when (treesit-ready-p 'haskell) (add-to-list 'auto-mode-alist '("\\.hs\\'" . haskell-ts-mode))) (provide 'haskell-ts-mode) ;;; haskell-ts-mode.el ends here --=-=-= Content-Type: text/plain (sorry for sending multipel time, I forgot I had to sent to the bug thing) -- Pranshu Sharma --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Wed Jan 01 20:22:42 2025 Received: (at control) by debbugs.gnu.org; 2 Jan 2025 01:22:42 +0000 Received: from localhost ([127.0.0.1]:41082 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tT9v8-0000DL-CF for submit@debbugs.gnu.org; Wed, 01 Jan 2025 20:22:42 -0500 Received: from mail-ej1-x635.google.com ([2a00:1450:4864:20::635]:53538) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1tT9v6-0000D3-9n for control@debbugs.gnu.org; Wed, 01 Jan 2025 20:22:40 -0500 Received: by mail-ej1-x635.google.com with SMTP id a640c23a62f3a-aaef00ab172so1022725466b.3 for ; Wed, 01 Jan 2025 17:22:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1735780954; x=1736385754; darn=debbugs.gnu.org; h=to:subject:message-id:date:mime-version:from:from:to:cc:subject :date:message-id:reply-to; bh=UTE4Uj2/Ej8Cm9nPTi+9UPxGrb/b+p/7wKt1st1mbXs=; b=SXVGEFjRGeTRnHzCK89aiRWsI0AoehTwe+2N/K5s7+JkccSuLyv4y9XmvZJKf5dY1A s9QRDwbli5isoirCB6QsMCGN4NtY91+McPDYn6jFLGDKGyxlNPVIolFwCzesWgsykkm6 DhrA231ef1D4dYmk1w6Gd3pl1eEMAzT566OmF0qdZrG6P4QAFpNcMoz0IjCkbTFpVQF9 qLzp8V/mJiI2uhNXA0qvrQL/ggm5MHeagxjWx76wBmlKpw+9P9/4IsPsGBEJyfOLUy4/ VxDoyvsyPKM0Fai1cjsV7Kgm3jp490o1RjAWt8CD/X7g6s9TKtea1mt/nvYmsnZlE2G6 Visw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1735780954; x=1736385754; h=to:subject:message-id:date:mime-version:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=UTE4Uj2/Ej8Cm9nPTi+9UPxGrb/b+p/7wKt1st1mbXs=; b=f5Xw56Yn/bleN/tnRGHuvU+x34HdK21buGpCY0Xlz8coMKbj75oUJRelS961rrw4jO e72+FjRBaO6RDDloDB+5VW0A4alWtTZHqks1Q8TphT2qXczb9LKOZR26xnCFuDGbSGpy HxMDdRManuzju9vygLxrBjcEDakrlUC00kMRLBFs4F235vUkwEpmeukEOA0aKdbV9gHr jY1qxt2Nxe44VepO8sIIy9owHLOcT4f6232Np5ORgVYKTJB+37FCq75Qp/qQ1m1HuBW9 VbJD4wGtHOAz4bmKgczwy99ZkQSyJqhnYE4tuoc8h3RGs3WkyoqRPGwUAoihpm8/cZLo nvLw== X-Gm-Message-State: AOJu0YyLuSlPf5888rCWzP3OvUM2wfImwxvV8+06gK/6EzqQBZdihSWk f6uzso0LPae2KGX1jCXpgDSTUn4L9lPUVYNgWmBUD/S/XjZVG4Mrc98Q7EccIU1ensZiAFz+BgC vbw6rcAqzHlCzwNuzKRF7ufs7oxrAliIM X-Gm-Gg: ASbGncv4R2ENfA+w1dkzaAVc8VflBENTrEKEL4udaKXTG+uipqshE1J2+Z1TPhFWu+T QaRQ+17hrEBj+YQrpFyH4hudphfi6o7Ach5qIVvKR X-Google-Smtp-Source: AGHT+IEWzLfMML2l7E/Ach/Y4tkmBT9WGFQu4+nNX0urTnSKFtPTWs4mZ3jdDWbmS9IatkIvm24F5nZsmoi7sVLsLa4= X-Received: by 2002:a05:6402:2790:b0:5d0:e73c:b7f0 with SMTP id 4fb4d7f45d1cf-5d81de1c241mr110165279a12.28.1735780954103; Wed, 01 Jan 2025 17:22:34 -0800 (PST) Received: from 753933720722 named unknown by gmailapi.google.com with HTTPREST; Wed, 1 Jan 2025 19:22:33 -0600 From: Stefan Kangas MIME-Version: 1.0 Date: Wed, 1 Jan 2025 19:22:33 -0600 Message-ID: Subject: control message for bug #75184 To: control@debbugs.gnu.org Content-Type: text/plain; charset="UTF-8" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: control X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) severity 75184 wishlist quit From debbugs-submit-bounces@debbugs.gnu.org Sat Mar 01 23:19:46 2025 Received: (at 75184) by debbugs.gnu.org; 2 Mar 2025 04:19:46 +0000 Received: from localhost ([127.0.0.1]:49764 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1toanp-0008Sl-OA for submit@debbugs.gnu.org; Sat, 01 Mar 2025 23:19:45 -0500 Received: from mail-ed1-x532.google.com ([2a00:1450:4864:20::532]:55795) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1toann-0008S2-I8 for 75184@debbugs.gnu.org; Sat, 01 Mar 2025 23:19:44 -0500 Received: by mail-ed1-x532.google.com with SMTP id 4fb4d7f45d1cf-5dedd4782c6so6412579a12.3 for <75184@debbugs.gnu.org>; Sat, 01 Mar 2025 20:19:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740889177; x=1741493977; darn=debbugs.gnu.org; h=cc:to:subject:message-id:date:mime-version:references:in-reply-to :from:from:to:cc:subject:date:message-id:reply-to; bh=n02wXtUvkegX87t1qRjHoWtpho+EIK4buqFAdjRJdDc=; b=RengXqC+8shWDn+qlLnz1cKR2J/amX9RjGqjVNOyu2UGoLRUzS+WIfjzCT7VHlL1ZE vZoX2nHioDpNd0rbbWmY8y30+HZUFNl1BHr6TCBMumcUmzKmn+IKC+3yA4TljntxTbjx 5y947w6ZJwJFk3/51pkf3OT6j0kMzkrhDxDXCaonYkY6AEZQcsj/e2htOoo0OAfWoxir bFGET5oTj0zU7TVBRxpxCEBfbUQ0zkr5vRRA+rV3tXvudwWEw6gVtvY3WtCz17xKl5Yb A01w3tbAavBJ0TZ0JPdHsceNG5TVK6bUOOFsAmqyg3s40Ufjgf4LDrB/uKYTZYyG+99p 1dJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740889177; x=1741493977; h=cc:to:subject:message-id:date:mime-version:references:in-reply-to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=n02wXtUvkegX87t1qRjHoWtpho+EIK4buqFAdjRJdDc=; b=hYoP7eHFePpZ91zH4bxdsHw9mjgaAU3UpEKtnXppSHstvbvlyqQ6wPzZFa39k4q3HM QaTulFIcjbD194AgM/q/lK+ISr8MuEKqXiQTah1AfcrVKDzXew9fDbBu18xZAH8NsNWf x48Fcz8RMFqE2vqQ0WyilsH7v2MAptsjKFvCUCqcyuYH3oSwRoHow4QBYhFHXHEEVtYs aqa1pvwmpTu7sEpwJgyADuNAxKAeEbdZ6riBqXGzh29gtkzhO9boqFBKzn0ZTkd4Tkj3 6z3wAuwK2ECFzGvks4yzhzYB6BBLSXa16jPxayLGzQojwcJuXzim+1dJPkLrAC36RDi8 UC6A== X-Gm-Message-State: AOJu0YxvVnPqx4WgqAN56Vpm3aRa4QpcibN0c4uMNY90tvxw2ODYVc2D Gd049ZwyYGIP7jQyaHWj34ieOfx0nZT+86DDKC7HryUB1ShaBxELwnZUmLdsHI5HXi3AtpyXnGw u9IMwp7gljuvNK7s+zHZ/yNvSbJiv9IWQMUI= X-Gm-Gg: ASbGncvDHrntO+Fi8tUAKCKqD4lz9/gNEr7EA79IK677lCYfMPwqf1oZxqY67ApiHXG ss+J3o9H5UW3OTh7sXgQI+Ti5RYV8EVhKPmHfQwCYmS5shkrtLA4Vwb5XhRHk8ZTBQ/UcPBM2ua bn3+wnqZ7eti/v/OQ9FyAUmHuFqQ== X-Google-Smtp-Source: AGHT+IGqnVNbyy7z3mjdPgYZOJXMPoFyAJxDLIqEJqw8SSxX+QjnRtd1M4fKt8lTjUuezf+wB8oIEpWnoppF7szdApg= X-Received: by 2002:a05:6402:4582:b0:5db:f26d:fff8 with SMTP id 4fb4d7f45d1cf-5e4d6b70ce7mr11108196a12.22.1740889177413; Sat, 01 Mar 2025 20:19:37 -0800 (PST) Received: from 753933720722 named unknown by gmailapi.google.com with HTTPREST; Sat, 1 Mar 2025 20:19:37 -0800 From: Stefan Kangas In-Reply-To: <87v7v27bu9.fsf@bauherren.ovh> References: <87v7v27bu9.fsf@bauherren.ovh> MIME-Version: 1.0 Date: Sat, 1 Mar 2025 20:19:37 -0800 X-Gm-Features: AQ5f1Jr13d1nHijpkV4cr1RDJh-_XqBbkh6IWkbkkb-CosDvR6dzKWA2D7MCnHU Message-ID: Subject: Re: bug#75184: Merge haskell-ts-mode to master To: Pranshu Sharma Content-Type: text/plain; charset="UTF-8" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 75184 Cc: 75184@debbugs.gnu.org, Yuan Fu X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Pranshu Sharma writes: > Hello all, > > This is request to merge haskell-ts-mode to master. The code is here > https://codeberg.org/pranshu/haskell-ts-mode , but I also attacched > file. I suggest you look in codeberg repo for latest changes. > > Phillip kauldarlic has done a review of the non-treesit specific part of > the code in emacs-devel, *I need someone* to review treesit specific > parts of it now. Yuan, could you please take a look at this and install it if it looks okay? Thanks in advance. From debbugs-submit-bounces@debbugs.gnu.org Sat Mar 01 23:19:50 2025 Received: (at control) by debbugs.gnu.org; 2 Mar 2025 04:19:50 +0000 Received: from localhost ([127.0.0.1]:49767 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1toanu-0008TM-4h for submit@debbugs.gnu.org; Sat, 01 Mar 2025 23:19:50 -0500 Received: from mail-ed1-x534.google.com ([2a00:1450:4864:20::534]:55349) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1toans-0008SY-0F for control@debbugs.gnu.org; Sat, 01 Mar 2025 23:19:48 -0500 Received: by mail-ed1-x534.google.com with SMTP id 4fb4d7f45d1cf-5ded1395213so5654313a12.2 for ; Sat, 01 Mar 2025 20:19:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740889182; x=1741493982; darn=debbugs.gnu.org; h=to:subject:message-id:date:mime-version:from:from:to:cc:subject :date:message-id:reply-to; bh=e+0w2q0DZqw2NyxhUIxce9urSAX2lOp+yeRcKQ4SU/g=; b=V5HzNjb+YQMiETqUQYhYCUI6I4ETomHwrT8S6/VNQ37m9KY1TgptV/tnZ7NONgkdZF A7oqQH86j9RtXV3sMH1ZpIphE9H/+Gxg+8s3zW5Iogv7muNTNXlf0Q7Div9ZXMP4Mjjk 9+QxxFiMxH2qwfMmO0YJbkjAcR3EY6jEs3FZC+mS0V/8CLsWRdtowtTXO63Hk+ZyqSu5 wqdKJi1+uMnHY2QetsFaj3rcUZov63XBJUU4TwGSaJqaBZAWWkSnzfgYi8YczAza25cK U6F4q/nXUSbTirRNEvLGG2/olteOZ1GHEO4GEwDtODQ+KIblQ3PCil+qKDsE9JmjbNEF hWnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740889182; x=1741493982; h=to:subject:message-id:date:mime-version:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=e+0w2q0DZqw2NyxhUIxce9urSAX2lOp+yeRcKQ4SU/g=; b=WozrX+tzw9tr3vgoG8wtIK9G7vlRGFLlGeXqaKxFwUsrldwx+SANBtMiaH85T8ZKsy row8H3Ju8BhmgjMn59rlByTDGYIOTvSkRE4qLTLJ0U79I89em6Hbj4BJc1O9vJzptIVj FUdPCAYmgbG50zgLB8Cc9DGAfMjioBPytBPa1OD9nf9/cc7G39dJq1kQueTJP5wc7bWK cVTcknVBPt/vxfskeGi3hP71H9WZrx+Fq2ecCOovZOktSjb9KN05oI2ywyTAAVhetWsT Y6jBk98vG6vk9VK0QpsDMqavl3fgE7xAOQJQbgKClNjFJnvRJ0xdl5239DGVGm3+ZfZX qTvw== X-Gm-Message-State: AOJu0Yx6E4TCxe/dj5C60svz31W6lPZJCi4lZa1Gt+ZY8YbDuf+LlZAu Pb3MpW32PYlSDkDx2k3T3mrDsz3TK+LX53ey7Se7VRozJMa1Z3BVlY+zUSNgwyS4/4Ws/fdB1Th EbK2wzw4q8QzPAPLgQcqrNtJnh2W978qLAoY= X-Gm-Gg: ASbGncuqJWbiTB1oEJcCtuBrzOFNqsE26aXNb7WKAIH3wFy9k1LiygNwKiinNumE/14 p3qfBuh24wp7ZplX9KHb8EOGlJm+rQjI1/ab6XKVcAq5NW5+bPDnnNt/h6RrU+AyXXG6f5HWfsY 7WiXXCcxCTUviZj/4Bs+jLmH7iZA== X-Google-Smtp-Source: AGHT+IHunLMZeETD1Ou9qIJk3O5mWzlmzZLinZxkpEMfHPEothlfy0NQyYBB2n3Yj1wlOHROLRnsjRSYik2WNkuQ0Ks= X-Received: by 2002:a17:907:7fa5:b0:abe:cbb7:d941 with SMTP id a640c23a62f3a-abf2682638emr1203150766b.57.1740889181810; Sat, 01 Mar 2025 20:19:41 -0800 (PST) Received: from 753933720722 named unknown by gmailapi.google.com with HTTPREST; Sat, 1 Mar 2025 20:19:41 -0800 From: Stefan Kangas MIME-Version: 1.0 Date: Sat, 1 Mar 2025 20:19:41 -0800 X-Gm-Features: AQ5f1JpyjZQH7bWiWMjUpjgUsL7pyUAerR69_hyC4sAphwn-Qy4sTebgKei8evA Message-ID: Subject: control message for bug #75184 To: control@debbugs.gnu.org Content-Type: text/plain; charset="UTF-8" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: control X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) tags 75184 + patch quit From debbugs-submit-bounces@debbugs.gnu.org Wed Mar 05 10:04:50 2025 Received: (at 75184) by debbugs.gnu.org; 5 Mar 2025 15:04:50 +0000 Received: from localhost ([127.0.0.1]:39051 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tpqIk-0004Zn-7O for submit@debbugs.gnu.org; Wed, 05 Mar 2025 10:04:50 -0500 Received: from mail-pj1-x102c.google.com ([2607:f8b0:4864:20::102c]:58764) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84_2) (envelope-from ) id 1tpqIX-0004YX-Nw for 75184@debbugs.gnu.org; Wed, 05 Mar 2025 10:04:38 -0500 Received: by mail-pj1-x102c.google.com with SMTP id 98e67ed59e1d1-2feb1d7a68fso12629136a91.1 for <75184@debbugs.gnu.org>; Wed, 05 Mar 2025 07:04:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741187071; x=1741791871; darn=debbugs.gnu.org; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=GRPA6bwYg426mVPbGuNghsO7W4vMoGGXANQ7dOdrz8A=; b=bbp7m7WWCbW/8l4GXIxSDV4qBsbj0Q0l1s8jDJL5XVj4n75KSzqY9Dpkmed3YhDI9h tFfV1XLr/VvGYjR1i5YZv7TpCIiPRjSsOizELZZ5ydlVcoLvueloc+H/Q04Psahot90A PegI977OFLncsm+w9yHwft4xqcUeVPBaEqqHbqonTnado6FA7Z/D9ROmp7so5e8V7xCy Wpf1HFvv/cUawW88oa05mj6516iJd43JZ+l/0DlblycOmTQDHpM/DGxnYrleOsubiYn6 Wiq3zRfOALw4DMTgSam+t0hqzOtIb7P4GEnnBbzRZuLY4p9y86Chcz6RhDiCMaQMMW0/ CxZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741187071; x=1741791871; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=GRPA6bwYg426mVPbGuNghsO7W4vMoGGXANQ7dOdrz8A=; b=JgSe2tcSWkvlUgDR2lAC3ZStXzVnugOI7NY1SVgRnDoTaHQKNvXwR41bgMLlNNu2pV 62fefyzFXMOneoCMphKGZCBbV/uGhDeWCLN/afue8IS2VzMlaqIe+lyStlooJy41tfop s/6XuQ+LU8q6vHJKDXGsUszJE28mEGU7Rl6Ais0rsTcluzevhBwRXt53lL3x7MDN07nK 55gG3d0ED2oLpOsNzE8gZ1khHp57Q/aZ0YE7jyr/Wh9z4QNnLKsZ7i2YyIZ9vCwjNyUE aI+r08y+LxHPGsmKnQ/VaUv+4/ueU+McNAwIFfmjcrlxujZ2YUtg+cmQCHSiwwTcos/J brbg== X-Forwarded-Encrypted: i=1; AJvYcCUbAIcquCvCAC1JtObxaP/cbH7Kp3e0mTonbQ9dhoB5dh4IfvPoNWEk9W5odPt6sI63jJQYAQ==@debbugs.gnu.org X-Gm-Message-State: AOJu0Yxp6fB92TMJqMxjCVWzTRzzEYJ8CcUJWnW/3pM7k8n5QMKtz6cy yT/WoCaTeU6RMc34gYCneWW/k6aVHAsiRTEjh4ca8bhz2dfH7ApkbfmBnw== X-Gm-Gg: ASbGncsK12LhIX+f0MKC08+OdSXgyCrOZp0PVUSkNQpTgMFfjAIWp9P3PZw/Vq4G7lf xMrG7qUyQc7Gv5d7ndnqFHxMAhu3WJf4kZgh1Qc5000V0naQcC44HetxzPDSX8EmCpK9Ry2MYvN a17sGk4agSPHUl+pw01jV6zMHarUXoeMYLAiKkAxfEao3u3QwOVgk0DtFfcQ42IQvHT4Gd7gDkI HCM3G2tApy0Sp0VfebuCWhuoyHmU5L5YwsydQwydbcnbrXnjQhV+5GEpM3lsKXi7Oc0fv6sdtN7 AwkLyrZRrbwgXAe0NxdoCoEh+9rPZU606b4i6zTvygnK8QcHmQbFshoddc/tjthFWTeA X-Google-Smtp-Source: AGHT+IFg3cZDKVSFw5akumBNIyOFgcQSXhRORysYLQ7NVnU7B0le5hFLebwk/BNlYnrVs+REYRwsaQ== X-Received: by 2002:a05:6a21:8dc2:b0:1ee:be88:f5cf with SMTP id adf61e73a8af0-1f34958cc72mr6925204637.32.1741187070085; Wed, 05 Mar 2025 07:04:30 -0800 (PST) Received: from smtpclient.apple ([2601:646:8f81:6120:99d4:b80d:b1d4:1c3f]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-aee7dec6727sm12147115a12.51.2025.03.05.07.04.28 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 05 Mar 2025 07:04:29 -0800 (PST) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51\)) Subject: Re: bug#75184: Merge haskell-ts-mode to master From: Yuan Fu In-Reply-To: Date: Wed, 5 Mar 2025 07:04:15 -0800 Content-Transfer-Encoding: quoted-printable Message-Id: References: <87v7v27bu9.fsf@bauherren.ovh> To: Stefan Kangas X-Mailer: Apple Mail (2.3776.700.51) X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 75184 Cc: 75184@debbugs.gnu.org, Pranshu Sharma X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) > On Mar 1, 2025, at 8:19=E2=80=AFPM, Stefan Kangas = wrote: >=20 > Pranshu Sharma writes: >=20 >> Hello all, >>=20 >> This is request to merge haskell-ts-mode to master. The code is here >> https://codeberg.org/pranshu/haskell-ts-mode , but I also attacched >> file. I suggest you look in codeberg repo for latest changes. >>=20 >> Phillip kauldarlic has done a review of the non-treesit specific part = of >> the code in emacs-devel, *I need someone* to review treesit specific >> parts of it now. >=20 > Yuan, could you please take a look at this and install it if it looks > okay? >=20 > Thanks in advance. Thanks for the ping Stefan. The patch mostly looks good! Since we=E2=80=99= re merging it to master, it can take advantage of some new features. For = example, it can add some definitions to treesit-thing-settings. Also, it = should set treesit-primary-parser. I also noticed that the source file uses tab indentation. Does Emacs = have any policy regarding tab/space indentation for lisp files? Yuan From debbugs-submit-bounces@debbugs.gnu.org Fri Mar 14 10:16:56 2025 Received: (at 75184) by debbugs.gnu.org; 14 Mar 2025 14:16:56 +0000 Received: from localhost ([127.0.0.1]:35417 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tt5qI-0007Dm-5t for submit@debbugs.gnu.org; Fri, 14 Mar 2025 10:16:55 -0400 Received: from [2001:19f0:7402:1636:5400:5ff:fe1b:1a90] (port=41740 helo=mail.bauherren.ovh) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1tt5qD-0007DM-5u for 75184@debbugs.gnu.org; Fri, 14 Mar 2025 10:16:51 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=bauherren.ovh; s=mail; t=1741961804; bh=jexhtcz9BFfekEl9gzQ8gXaGmgN1IEveqC3L/3CfKsE=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=ZGEek3122sIqa03Mr33LaUPzVpx8Ppv9ZDi1yoD3I5v2x0RZKW04qoIgXCnI2dQ22 Zb8nm8Gp7qf/LMZD6JC7IkRJj6SLQUBAx8+iwanhAs723+UjhvP1O1S/g51I1VOsk+ 492BS7Dz8IyMk/CId0AMVxBOLj9S39iuX91rCRjkYNOcmaVFGOlXXvB2iM+lJRe5kQ FzMf+AnjJiw1jFSDTYjZJgkYdh8WJlDXkBucPxlfZSOsehoa1jFOK1mUe5IK4O2b97 1izhAc/5XEwUqumdNs5FQ0x0XX9XV1nTj9fLt15AEUw3V0ds/c1b+ntWKjIiEMx5A8 njTxoZTSNLMew== From: pranshu sharma To: Yuan Fu Subject: Re: bug#75184: Merge haskell-ts-mode to master In-Reply-To: References: <87v7v27bu9.fsf@bauherren.ovh> Date: Sat, 15 Mar 2025 00:16:36 +1000 Message-ID: <878qp7hf3v.fsf@bauherren.ovh> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Yuan Fu writes: > Thanks for the ping Stefan. The patch mostly looks good! Since we’re > merging it to master, it can take advantage of some new features. For > example, it can add some definitions to treesit-thing [...] Content analysis details: (1.3 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Debbugs-Envelope-To: 75184 Cc: 75184@debbugs.gnu.org, Stefan Kangas X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: 0.3 (/) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Yuan Fu writes: > Thanks for the ping Stefan. The patch mostly looks good! Since we=E2=80= =99re > merging it to master, it can take advantage of some new features. For > example, it can add some definitions to treesit-thing-settings. Also, > it should set treesit-primary-parser. Thanks for review, Lots of changes have been made from ~3 months ago when I posted that file, the treesit-thing-settings has already been adopted. I'll attatch an updated file. > I also noticed that the source file uses tab indentation. Does Emacs > have any policy regarding tab/space indentation for lisp files? I think tabs don't make sense in lisp file, I untabified the code. --=-=-= Content-Type: text/plain; charset=utf-8 Content-Disposition: inline; filename=haskell-ts-mode.el Content-Transfer-Encoding: quoted-printable Content-Description: Haskell ts mode ;;; haskell-ts-mode.el --- A treesit based major mode for haskell -*- lexic= al-binding:t -*- ;; Copyright (C) 2024 Pranshu Sharma ;; Author: Pranshu Sharma ;; URL: https://codeberg.org/pranshu/haskell-ts-mode ;; Package-Requires: ((emacs "29.3")) ;; Version: 1 ;; Keywords: languages, haskell ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; This is a major mode that uses treesitter to provide all the basic ;; major mode stuff, like indentation, font lock, etc... ;; It uses the grammer at: https://github.com/tree-sitter/tree-sitter-haske= ll ;;; Code: (require 'comint) (require 'treesit) (declare-function treesit-parser-create "treesit.c") (declare-function treesit-node-start "treesit.c") (declare-function treesit-node-parent "treesit.c") (declare-function treesit-node-prev-sibling "treesit.c") (declare-function treesit-node-next-sibling "treesit.c") (declare-function treesit-node-end "treesit.c") (declare-function treesit-node-child "treesit.c") (declare-function treesit-node-type "treesit.c") (defgroup haskell-ts-mode nil "Group that contains haskell-ts-mode variables" :group 'langs) (defcustom haskell-ts-ghci "ghci" "The command to be called to run ghci." :type 'string) (defcustom haskell-ts-ghci-buffer-name "Inferior Haskell" "Buffer name for the ghci prcoess." :type 'string) (defcustom haskell-ts-use-indent t "Set to non-nil to use the indentation provided by haskell-ts-mode" :type 'boolean) (defcustom haskell-ts-font-lock-level 4 "Level of font lock, 1 for minimum highlghting and 4 for maximum." :type '(choice (const :tag "Minimal Highlighting" 1) (const :tag "Low Highlighting" 2) (const :tag "High Highlighting" 3) (const :tag "Maximum Highlighting" 4))) (defcustom haskell-ts-prettify-words nil "Prettify some words to unicode symbols. This will concat `haskell-ts-prettify-words-alist' to `prettify-symbols-alist' in `haskell-ts-mode'." :type 'boolean) (defvar haskell-ts-font-lock-feature-list `((comment str pragma parens) (type definition function args module import operator) (match keyword) (otherwise signature type-sig))) (defvar haskell-ts-prettify-symbols-alist '(("\\" . "=CE=BB") ("/=3D" . "=E2=89=A0") ("->" . "=E2=86=92") ("=3D>" . "=E2=87=92") ("<-" . "=E2=86=90") ("<=3D" . "=E2=89=A4") (">=3D" . "=E2=89=A5") ("/<" . "=E2=89=AE") ("/>" . "=E2=89=AF") ("&&" . "=E2=88=A7") ("||" . "=E2=88=A8") ("=3D=3D" . "=E2=89=A1")) "`prettify-symbols-alist' for `haskell-ts-mode'. This variable contains all the symbol for `haskell-ts-mode' to unicode character. See `haskell-ts-prettify-words-alist' for mappign words to alternative unicode character.") (defvar haskell-ts-prettify-words-alist '(("forall" . "=E2=88=80") ("exists" . "=E2=88=83") ("elem" . "=E2=88=88") ("notElem" . "=E2=88=89") ("member" . "=E2=88=88") ("notMember" . "=E2=88=89") ("union" . "=E2=88=AA") ("intersection" . "=E2=88=A9") ("isSubsetOf" . "=E2=8A=86") ("isProperSubsetOf" . "=E2=8A=82") ("mempty" . "=E2=88=85")) "Additional symbols to prettify for `haskell-ts-mode'. This is added to `prettify-symbols-alist' for `haskell-ts-mode' buffers when `haskell-ts-prettify-words' is non-nil.") (defvar haskell-ts-font-lock (treesit-font-lock-rules :language 'haskell :feature 'keyword `(["module" "import" "data" "let" "where" "case" "type" "if" "then" "else" "of" "do" "in" "instance" "class" "newtype"] @font-lock-keyword-face) :language 'haskell :feature 'otherwise :override t `(((match (guards guard: (boolean (variable) @font-lock-keyword-face))) (:match "otherwise" @font-lock-keyword-face))) ;; TODO: It is weird that we use operator face for parenthesses and also= for operators. ;; I see two other, possibly better solutions: ;; 1. Use delimiter face for parenthesses, ::, -> and similar, and ope= rator face for operators. ;; 2. Keep using operator face for parenthesses and co, but use ;; function call face for operators (since they are functions at ;; the end). :language 'haskell :feature 'operator '((operator) @font-lock-operator-face) :language 'haskell :feature 'module '((module (module_id) @font-lock-type-face)) :language 'haskell :feature 'import '((import ["qualified" "as"] @font-lock-keyword-face)) :language 'haskell :feature 'type-sig '((signature (binding_list (variable) @font-lock-doc-markup-face)) (signature (variable) @font-lock-doc-markup-face)) :language 'haskell :feature 'args :override 'keep '((function (infix left_operand: (_) @haskell-ts--fontify-arg)) (function (infix right_operand: (_) @haskell-ts--fontify-arg)) (generator :anchor (_) @haskell-ts--fontify-arg) (patterns) @haskell-ts--fontify-arg) :language 'haskell :feature 'type '((type) @font-lock-type-face (constructor) @font-lock-type-face (declarations (type_synomym (name) @font-lock-type-face)) (declarations (data_type name: (name) @font-lock-type-face))) :language 'haskell :override t :feature 'signature '((signature (function) @haskell-ts--fontify-type) (context (function) @haskell-ts--fontify-type) (signature "::" @font-lock-operator-face)) :language 'haskell :feature 'match `((match ("|" @font-lock-doc-face) ("=3D" @font-lock-doc-face)) (list_comprehension ("|" @font-lock-doc-face (qualifiers (generator "<-" @font-lock-doc-face))= )) (match ("->" @font-lock-doc-face))) :language 'haskell :feature 'comment `(((comment) @font-lock-comment-face) ((haddock) @font-lock-doc-face)) :language 'haskell :feature 'pragma `((pragma) @font-lock-preprocessor-face (cpp) @font-lock-preprocessor-face) :language 'haskell :feature 'str :override t `((char) @font-lock-string-face (string) @font-lock-string-face (quasiquote (quoter) @font-lock-type-face) (quasiquote (quasiquote_body) @font-lock-preprocessor-face)) :language 'haskell :feature 'parens :override t `(["(" ")" "[" "]"] @font-lock-operator-face (infix operator: (_) @font-lock-operator-face)) :language 'haskell :feature 'function :override t '((function name: (variable) @font-lock-function-name-face) (function (infix (operator) @font-lock-function-name-face)) (function (infix (infix_id (variable) @font-lock-function-name-face))) (bind :anchor (_) @haskell-ts--fontify-params) (function arrow: _ @font-lock-operator-face))) "The treesitter font lock settings for haskell.") (defun haskell-ts--stand-alone-parent (_ parent bol) (save-excursion (goto-char (treesit-node-start parent)) (let ((type (treesit-node-type parent))) (if (and (not bol) (or (looking-back "^[ \t]*" (line-beginning-position)) (member type '("when" "where" "do" "let" "local_binds" "function")))) (treesit-node-start parent) (haskell-ts--stand-alone-parent 1 (funcall (if bol #'treesit-node-parent #'= identity) (treesit-node-parent parent)) nil))))) (defvar haskell-ts--ignore-types (regexp-opt '("comment" "cpp" "haddock")) "Node types that will be ignored by indentation.") (defvar haskell-ts-indent-rules (let* ((p-sib (lambda (node &optional arg) (let* ((func (if arg #'treesit-node-prev-sibling #'treesit-node-next-sibling)) (n (funcall func node))) (while (and n (string-match haskell-ts--ignore-types (treesit-node-type n))) (setq n (funcall func n))) n))) (p-prev-sib (lambda (node &optional _ _) (treesit-node-start (funcall p-sib n= ode t)))) (p-n-prev (lambda (node) (funcall p-sib node t))) (parent-first-child (lambda (_ parent _) (treesit-node-start (treesit-node-child pare= nt 0))))) `((haskell ((node-is "^cpp$") column-0 0) ((parent-is "^comment$") column-0 0) ((parent-is "^haddock$") column-0 0) ((parent-is "^imports$") column-0 0) ;; Infix ((n-p-gp nil "infix" "infix") (lambda (_ node _) (let ((first-inf nil)) (while (string=3D "infix" (treesit-node-type (setq node (treesit-node-parent node)))) (setq first-inf node)) (funcall ,parent-first-child nil first-inf nil))) 0) ((node-is "^infix$") ,parent-first-child 0) ;; Lambda ((parent-is "^lambda\\(_case\\)?$") standalone-parent 2) ((parent-is "^class_declarations$") prev-sibling 0) ((node-is "^where$") parent 2) ;; in ((node-is "^in$") parent 0) ((parent-is "qualifiers") parent 0) ;; list ((node-is "^]$") parent 0) ((parent-is "^list$") standalone-parent 2) ;; If then else ((node-is "^then$") parent 2) ((node-is "^else$") parent 2) ((parent-is "^apply$") haskell-ts--stand-alone-parent 1) ((node-is "^quasiquote$") grand-parent 2) ((parent-is "^quasiquote_body$") (lambda (_ _ c) c) 0) ((lambda (node parent bol) (when-let ((n (treesit-node-prev-sibling node))) (while (string=3D "comment" (treesit-node-type n)) (setq n (treesit-node-prev-sibling n))) (string=3D "do" (treesit-node-type n)))) haskell-ts--stand-alone-parent 3) ((parent-is "^do$") ,p-prev-sib 0) ((parent-is "^alternatives$") ,p-prev-sib 0) ;; prev-adaptive-prefix is broken sometimes (no-node (lambda (_ _ _) (save-excursion (goto-char (line-beginning-position 0)) (back-to-indentation) (point))) 0) ((parent-is "^data_constructors$") parent 0) ;; where ((lambda (node _ _) (let ((n (treesit-node-prev-sibling node))) (while (string=3D "comment" (treesit-node-type n)) (setq n (treesit-node-prev-sibling n))) (string=3D "where" (treesit-node-type n)))) (lambda (_ b _) (+ 1 (treesit-node-start (treesit-node-prev-sibling b)))) 3) ((parent-is "local_binds\\|instance_declarations") ,p-prev-sib 0) ;; Match ((lambda (node _ _) (and (string=3D "match" (treesit-node-type node)) (string-match (regexp-opt '("patterns" "variable")) (treesit-node-type (funcall ,p-n-prev node))))) standalone-parent 2) ((node-is "match") ,p-prev-sib 0) ((parent-is "match") standalone-parent 2) ((parent-is "^haskell$") column-0 0) ((parent-is "^declarations$") column-0 0) ((parent-is "^record$") standalone-parent 2) ((parent-is "^exports$") (lambda (_ b _) (treesit-node-start (treesit-node-prev-sibling b))) 0) ((n-p-gp nil "signature" "foreign_import") grand-parent 3) ((parent-is "^case$") standalone-parent 4) ((node-is "^alternatives$") (lambda (_ b _) (treesit-node-start (treesit-node-child b 0))) 2) ((node-is "^comment$") (lambda (node parent _) (pcase node ;; (relevent means type not it haskell-ts--ignore-types) ;; 1. next relevent sibling if exists ((app ,p-sib (and (pred (not null)) n)) (treesit-node-start n)) ;; 2. previous relevent sibling if exists ((app ,p-prev-sib (and (pred (not null)) n)) n) ;; 3. parent (_ (treesit-node-start parent)))) 0) ;; Backup (catch-all parent 2)))) "\"Simple\" treesit indentation rules for haskell.") (defvar haskell-ts-mode-syntax-table (eval-when-compile (let ((table (make-syntax-table)) (imenu-syntax-list `((" " " \t\n\r\f\v") ("_" "!#$%&*+./<=3D>?\\^|-~:") ("w" ?_ ?\') ("." ",:@") ("\"" ?\") ("()" ?\() (")(" ?\)) ("(]" ?\[) (")[" ?\]) ("$`" ?\`) ("(}1nb" ?\{ ) ("){4nb" ?\} ) ("_ 123" ?- ) (">" "\r\n\f\v")))) (dolist (ls imenu-syntax-list table) (dolist (char (if (stringp (cadr ls)) (string-to-list (cadr ls)) (cdr ls))) (modify-syntax-entry char (car ls) table))))) "The syntax table for haskell.") (defun haskell-ts-sexp (node) "Returns non-nil on a sexp node." (let ((node-text (treesit-node-text node 1))) (and (not (member node-text '( "{" "}" "[" "]" "(" ")" ";"))) (not (and (string=3D "operator" (treesit-node-field-name node)) (=3D 1 (length node-text))))))) (defvar haskell-ts-thing-settings `((haskell (sexp haskell-ts-sexp) (sentence "match") (string "string") (text "string"))) "`treesit-thing-settings' for `haskell-ts-mode'.") (defmacro haskell-ts-imenu-name-function (check-func) `(lambda (node) (let ((nn (treesit-node-child node 0 node))) (if (funcall ,check-func node) (if (string=3D (treesit-node-type nn) "infix") (treesit-node-text (treesit-node-child nn 1)) (haskell-ts-defun-name node)) nil)))) (defvar-keymap haskell-ts-mode-map :doc "Keymap for haskell-ts-mode." "C-c C-c" #'haskell-ts-compile-region-and-go "C-c C-r" #'run-haskell) ;;;###autoload (define-derived-mode haskell-ts-mode prog-mode "haskell ts mode" "Major mode for Haskell files using tree-sitter." :table haskell-ts-mode-syntax-table (unless (treesit-ready-p 'haskell) (error "Tree-sitter for Haskell is not available")) (setq treesit-primary-parser (treesit-parser-create 'haskell)) (setq treesit-language-at-point-function (lambda (&rest _) 'haskell)) (setq-local treesit-defun-type-regexp "\\(?:\\(?:function\\|struct\\)_def= inition\\)") ;; Indent (when haskell-ts-use-indent (setq-local treesit-simple-indent-rules haskell-ts-indent-rules) (setq-local indent-tabs-mode nil)) ;; Comment (setq-local comment-start "-- ") (setq-local comment-use-syntax t) (setq-local comment-start-skip "\\(?: \\|^\\)-+") ;; Electric (setq-local electric-pair-pairs '((?` . ?`) (?\( . ?\)) (?{ . ?}) (?\" . ?\") (?\[ . ?\]))) ;; Navigation (setq-local treesit-defun-name-function 'haskell-ts-defun-name) (setq-local treesit-thing-settings haskell-ts-thing-settings) (setq-local treesit-defun-type-regexp ;; Since haskell is strict functional, any 2nd level ;; entity is defintion (cons ".+" (lambda (node) (and (not (string-match haskell-ts--ignore-types (tre= esit-node-type node))) (string=3D "declarations" (treesit-node-type (tr= eesit-node-parent node))))))) (setq-local prettify-symbols-alist (append haskell-ts-prettify-symbols-alist (and haskell-ts-prettify-words haskell-ts-prettify-words-alist))) ;; Imenu (setq-local treesit-simple-imenu-settings `((nil haskell-ts-imenu-func-node-p nil ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-fu= nc-node-p)) ("Signatures.." haskell-ts-imenu-sig-node-p nil ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-sig-no= de-p)) ("Data..." haskell-ts-imenu-data-type-p nil (lambda (node) (treesit-node-text (treesit-node-child node 1)))))) ;; font-lock (setq-local treesit-font-lock-level haskell-ts-font-lock-level) (setq-local treesit-font-lock-settings haskell-ts-font-lock) (setq-local treesit-font-lock-feature-list haskell-ts-font-lock-feature-list) (treesit-major-mode-setup)) (defun haskell-ts--fontify-func (node face) (if (string=3D "variable" (treesit-node-type node)) (put-text-property (treesit-node-start node) (treesit-node-end node) 'face face) (mapc (lambda (n) (haskell-ts--fontify-func n face)) (treesit-node-children node)))) (defun haskell-ts--fontify-arg (node &optional _ _ _) (haskell-ts--fontify-func node 'font-lock-variable-name-face)) (defun haskell-ts--fontify-params (node &optional _ _ _) (haskell-ts--fontify-func node 'font-lock-function-name-face)) (defun haskell-ts--fontify-type (node &optional _ _ _) (let ((last-child (treesit-node-child node -1))) (if (string=3D (treesit-node-type last-child) "function") (haskell-ts--fontify-type last-child) (put-text-property (treesit-node-start last-child) (treesit-node-end last-child) 'face 'font-lock-variable-name-face)))) (defun haskell-ts-imenu-node-p (regex node) (and (string-match-p regex (treesit-node-type node)) (string=3D (treesit-node-type (treesit-node-parent node)) "declarati= ons"))) (defun haskell-ts-imenu-func-node-p (node) (haskell-ts-imenu-node-p "function\\|bind" node)) (defun haskell-ts-imenu-sig-node-p (node) (haskell-ts-imenu-node-p "signature" node)) (defun haskell-ts-imenu-data-type-p (node) (haskell-ts-imenu-node-p "data_type" node)) (defun haskell-ts-defun-name (node) (treesit-node-text (treesit-node-child node 0))) (defun haskell-ts-compile-region-and-go (start end) "Compile the text from START to END in the haskell proc." (interactive "r") (let ((hs (haskell-ts-haskell-session)) (str (buffer-substring-no-properties start end))) (comint-send-string hs ":{\n") (comint-send-string hs (replace-regexp-in-string "^:\\}" "\\:}" str nil t)) (comint-send-string hs "\n:}\n"))) ;;;###autoload (defun run-haskell () "Run an inferior Haskell process." (interactive) (let ((buffer (concat "*" haskell-ts-ghci-buffer-name "*"))) (pop-to-buffer-same-window (if (comint-check-proc buffer) gbuffer (make-comint haskell-ts-ghci-buffer-name haskell-ts-ghci nil buffer-= file-name))))) (defun haskell-ts-haskell-session () (get-buffer-process (concat "*" haskell-ts-ghci-buffer-name "*"))) (when (treesit-ready-p 'haskell) (add-to-list 'auto-mode-alist '("\\.hs\\'" . haskell-ts-mode))) (provide 'haskell-ts-mode) ;;; haskell-ts-mode.el ends here --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Fri Apr 18 09:59:00 2025 Received: (at 75184) by debbugs.gnu.org; 18 Apr 2025 13:59:01 +0000 Received: from localhost ([127.0.0.1]:50780 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1u5mF1-000667-9y for submit@debbugs.gnu.org; Fri, 18 Apr 2025 09:59:00 -0400 Received: from mail.bauherren.ovh ([209.250.229.59]:58526) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1u5mEj-00061k-Be for 75184@debbugs.gnu.org; Fri, 18 Apr 2025 09:58:48 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=bauherren.ovh; s=mail; t=1744984701; bh=wfJvtqfCtRjLsK4f1JOmb8MiqgoGrnh3vsLiJRQD//0=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=fsNj9Ud8b7HVRacnvLXqqVBuIqsLMoT3Y3C7Iyr/aSnyoJ9/U+f/Pc7sdS0/NwBmt BCLIKBr0guvsIE4q1Xt71ZaAXM8y41tNAloy1hM7oyYjTt8hOHaIe4xkaUhhECI4hd 5QOI3uJ6I1sZg1+1Ai22hVL9kRoS6saqL4dgv2B8QVWyfZcFUgVlW6al5xx5CzKFL7 +Xr4NgqRQAuiv4xzR0+7DKMfcu3mqTMXKH9aSDtuv5CtV3qzDndjBK5WZDyGr4hxNC BwOEs9jDVGuWKOLwS02QenjWQubbs85PDvGKwYAqryXo1G+GBc204AugqYAuhs85Id /Hx4cP5WQEOvg== From: Pranshu Sharma To: pranshu sharma via "Bug reports for GNU Emacs, the Swiss army knife of text editors" Subject: Re: bug#75184: Merge haskell-ts-mode to master In-Reply-To: <878qp7hf3v.fsf@bauherren.ovh> References: <87v7v27bu9.fsf@bauherren.ovh> <878qp7hf3v.fsf@bauherren.ovh> Date: Fri, 18 Apr 2025 23:58:14 +1000 Message-ID: <87sem5po5l.fsf@bauherren.ovh> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 75184 Cc: Yuan Fu , 75184@debbugs.gnu.org, Stefan Kangas X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain I have made final diff for emacs master that adds the mode as well as the news entry. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=haskell-ts-mode.diff Content-Transfer-Encoding: quoted-printable diff --git a/etc/NEWS b/etc/NEWS index af4829c..a4d74e2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1930,6 +1930,9 @@ A major mode based on 'conf-mode' for editing ".npmrc= " files. =20 ** New major modes based on the tree-sitter library =20 +*** New major mode 'haskell-ts-mode' +A major made based on the tree-sitter library for editing haskell files. + *** New major mode 'markdown-ts-mode'. A major mode based on the tree-sitter library for editing Markdown files. =20 diff --git a/lisp/progmodes/haskell-ts-mode.el b/lisp/progmodes/haskell-ts-= mode.el new file mode 100644 index 0000000..014f816 --- /dev/null +++ b/lisp/progmodes/haskell-ts-mode.el @@ -0,0 +1,559 @@ +;;; haskell-ts-mode.el --- A treesit based major mode for haskell -*- lexi= cal-binding:t -*- + +;; Copyright (C) 2024, 2025 Pranshu Sharma + +;; Author: Pranshu Sharma +;; URL: https://codeberg.org/pranshu/haskell-ts-mode +;; Package-Requires: ((emacs "29.3")) +;; Version: 1.01 +;; URL: https://codeberg.org/pranshu/haskell-ts-mode/ +;; Keywords: languages, haskell + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This is a major mode that uses treesitter to provide all the basic +;; major mode stuff, like indentation, font lock, etc... +;; It uses the grammer at: https://github.com/tree-sitter/tree-sitter-hask= ell + +;;; Code: + +(require 'comint) +(require 'treesit) + +(declare-function treesit-parser-create "treesit.c") +(declare-function treesit-node-start "treesit.c") +(declare-function treesit-node-parent "treesit.c") +(declare-function treesit-node-prev-sibling "treesit.c") +(declare-function treesit-node-next-sibling "treesit.c") +(declare-function treesit-node-end "treesit.c") +(declare-function treesit-node-child "treesit.c") +(declare-function treesit-node-type "treesit.c") + +(defgroup haskell-ts-mode nil + "Group that contains haskell-ts-mode variables" + :group 'langs) + +(defcustom haskell-ts-ghci "ghci" + "The command to be called to run ghci." + :type 'string) + +(defcustom haskell-ts-ghci-buffer-name "Inferior Haskell" + "Buffer name for the ghci prcoess." + :type 'string) + +(defcustom haskell-ts-use-indent t + "Set to non-nil to use the indentation provided by haskell-ts-mode" + :type 'boolean) + +(defcustom haskell-ts-font-lock-level 4 + "Level of font lock, 1 for minimum highlghting and 4 for maximum." + :type '(choice (const :tag "Minimal Highlighting" 1) + (const :tag "Low Highlighting" 2) + (const :tag "High Highlighting" 3) + (const :tag "Maximum Highlighting" 4))) + +(defcustom haskell-ts-prettify-words nil + "Prettify some words to unicode symbols. +This will concat `haskell-ts-prettify-words-alist' to +`prettify-symbols-alist' in `haskell-ts-mode'." + :type 'boolean) + +(defvar haskell-ts-font-lock-feature-list + `((comment str pragma parens) + (type definition function args module import operator) + (match keyword) + (otherwise signature type-sig))) + +(defvar haskell-ts-prettify-symbols-alist + '(("\\" . "=CE=BB") + ("/=3D" . "=E2=89=A0") + ("->" . "=E2=86=92") + ("=3D>" . "=E2=87=92") + ("<-" . "=E2=86=90") + ("<=3D" . "=E2=89=A4") + (">=3D" . "=E2=89=A5") + ("/<" . "=E2=89=AE") + ("/>" . "=E2=89=AF") + ("&&" . "=E2=88=A7") + ("||" . "=E2=88=A8") + ("=3D=3D" . "=E2=89=A1")) + "`prettify-symbols-alist' for `haskell-ts-mode'. +This variable contains all the symbol for `haskell-ts-mode' to unicode +character. See `haskell-ts-prettify-words-alist' for mappign words to +alternative unicode character.") + +(defvar haskell-ts-prettify-words-alist + '(("forall" . "=E2=88=80") + ("exists" . "=E2=88=83") + ("elem" . "=E2=88=88") + ("notElem" . "=E2=88=89") + ("member" . "=E2=88=88") + ("notMember" . "=E2=88=89") + ("union" . "=E2=88=AA") + ("intersection" . "=E2=88=A9") + ("isSubsetOf" . "=E2=8A=86") + ("isProperSubsetOf" . "=E2=8A=82") + ("mempty" . "=E2=88=85")) + "Additional symbols to prettify for `haskell-ts-mode'. +This is added to `prettify-symbols-alist' for `haskell-ts-mode' buffers +when `haskell-ts-prettify-words' is non-nil.") + +(defvar haskell-ts-font-lock + (treesit-font-lock-rules + :language 'haskell + :feature 'keyword + `(["module" "import" "data" "let" "where" "case" "type" + "if" "then" "else" "of" "do" "in" "instance" "class" "newtype"] + @font-lock-keyword-face) + :language 'haskell + :feature 'otherwise + :override t + `(((match (guards guard: (boolean (variable) @font-lock-keyword-face))) + (:match "otherwise" @font-lock-keyword-face))) + + ;; This needs to be positioned above where we apply + ;; font-lock-operator-face to comma + :language 'haskell + :override t + :feature 'signature + '((signature (function) @haskell-ts--fontify-type) + (context (function) @haskell-ts--fontify-type) + (signature "::" @font-lock-operator-face)) + + ;; TODO: It is weird that we use operator face for parenthesses and als= o for operators. + ;; I see two other, possibly better solutions: + ;; 1. Use delimiter face for parenthesses, ::, -> and similar, and op= erator face for operators. + ;; 2. Keep using operator face for parenthesses and co, but use + ;; function call face for operators (since they are functions at + ;; the end). + :language 'haskell + :feature 'operator + :override t + '((operator) @font-lock-operator-face + "," @font-lock-operator-face) + + :language 'haskell + :feature 'module + '((module (module_id) @font-lock-type-face)) + + :language 'haskell + :feature 'import + '((import ["qualified" "as"] @font-lock-keyword-face)) + + :language 'haskell + :feature 'type-sig + '((signature (binding_list (variable) @font-lock-doc-markup-face)) + (signature (variable) @font-lock-doc-markup-face)) + + :language 'haskell + :feature 'args + :override 'keep + '((function (infix left_operand: (_) @haskell-ts--fontify-arg)) + (function (infix right_operand: (_) @haskell-ts--fontify-arg)) + (generator :anchor (_) @haskell-ts--fontify-arg) + (patterns) @haskell-ts--fontify-arg) + + :language 'haskell + :feature 'type + '((type) @font-lock-type-face + (constructor) @font-lock-type-face + (declarations (type_synomym (name) @font-lock-type-face)) + (declarations (data_type name: (name) @font-lock-type-face)) + (declarations (newtype name: (name) @font-lock-type-face))) + + :language 'haskell + :feature 'match + `((match ("|" @font-lock-doc-face) ("=3D" @font-lock-doc-face)) + (list_comprehension ("|" @font-lock-doc-face + (qualifiers (generator "<-" @font-lock-doc-face)= ))) + (match ("->" @font-lock-doc-face))) + + :language 'haskell + :feature 'comment + `(((comment) @font-lock-comment-face) + ((haddock) @font-lock-doc-face)) + + :language 'haskell + :feature 'pragma + `((pragma) @font-lock-preprocessor-face + (cpp) @font-lock-preprocessor-face) + + :language 'haskell + :feature 'str + :override t + `((char) @font-lock-string-face + (string) @font-lock-string-face + (quasiquote (quoter) @font-lock-type-face) + (quasiquote (quasiquote_body) @font-lock-preprocessor-face)) + + :language 'haskell + :feature 'parens + :override t + `(["(" ")" "[" "]"] @font-lock-operator-face + (infix operator: (_) @font-lock-operator-face)) + + :language 'haskell + :feature 'function + :override t + '((function name: (variable) @font-lock-function-name-face) + (function (infix (operator) @font-lock-function-name-face)) + (function (infix (infix_id (variable) @font-lock-function-name-face))) + (bind :anchor (_) @haskell-ts--fontify-params) + (function arrow: _ @font-lock-operator-face))) + "The treesitter font lock settings for haskell.") + +(defun haskell-ts--stand-alone-parent (_ parent bol) + (save-excursion + (goto-char (treesit-node-start parent)) + (let ((type (treesit-node-type parent))) + (if (and (not bol) + (or (looking-back "^[ \t]*" (line-beginning-position)) + (member + type + '("when" "where" "do" "let" "local_binds" "function"))= )) + (treesit-node-start parent) + (haskell-ts--stand-alone-parent 1 (funcall + (if bol #'treesit-node-parent #= 'identity) + (treesit-node-parent parent)) + nil))))) + +(defvar haskell-ts--ignore-types + (regexp-opt '("comment" "cpp" "haddock")) + "Node types that will be ignored by indentation.") + +(defvar haskell-ts-indent-rules + (let* ((p-sib + (lambda (node &optional arg) + (let* ((func (if arg + #'treesit-node-prev-sibling + #'treesit-node-next-sibling)) + (n (funcall func node))) + (while (and n (string-match haskell-ts--ignore-types + (treesit-node-type n))) + (setq n (funcall func n))) + n))) + (p-prev-sib + (lambda (node &optional _ _) (treesit-node-start (funcall p-sib = node t)))) + (p-n-prev (lambda (node) (funcall p-sib node t))) + (parent-first-child (lambda (_ parent _) + (treesit-node-start (treesit-node-child par= ent 0))))) + `((haskell + ((node-is "^cpp$") column-0 0) + ((parent-is "^comment$") column-0 0) + ((parent-is "^haddock$") column-0 0) + ((parent-is "^imports$") column-0 0) + ;; Infix + ((n-p-gp nil "infix" "infix") + (lambda (_ node _) + (let ((first-inf nil)) + (while (string=3D "infix" + (treesit-node-type + (setq node (treesit-node-parent node)))) + (setq first-inf node)) + (funcall ,parent-first-child nil first-inf nil))) + 0) + ((node-is "^infix$") ,parent-first-child 0) + + ;; Lambda + ((parent-is "^lambda\\(_case\\)?$") standalone-parent 2) + + ((parent-is "^class_declarations$") prev-sibling 0) + + ((node-is "^where$") parent 2) + + ;; in + ((node-is "^in$") parent 0) + + ((parent-is "qualifiers") parent 0) + + ;; list + ((node-is "^]$") parent 0) + ((parent-is "^list$") standalone-parent 2) + + ;; If then else + ((node-is "^then$") parent 2) + ((node-is "^else$") parent 2) + + ((parent-is "^apply$") haskell-ts--stand-alone-parent 1) + ((node-is "^quasiquote$") grand-parent 2) + ((parent-is "^quasiquote_body$") (lambda (_ _ c) c) 0) + ((lambda (node parent bol) + (when-let ((n (treesit-node-prev-sibling node))) + (while (string=3D "comment" (treesit-node-type n)) + (setq n (treesit-node-prev-sibling n))) + (string=3D "do" (treesit-node-type n)))) + haskell-ts--stand-alone-parent + 3) + ((parent-is "^do$") ,p-prev-sib 0) + + ((parent-is "^alternatives$") ,p-prev-sib 0) + + ;; prev-adaptive-prefix is broken sometimes + (no-node + (lambda (_ _ _) + (save-excursion + (goto-char (line-beginning-position 0)) + (back-to-indentation) + (point))) + 0) + + ((parent-is "^data_constructors$") parent 0) + + ;; where + ((lambda (node _ _) + (let ((n (treesit-node-prev-sibling node))) + (while (string=3D "comment" (treesit-node-type n)) + (setq n (treesit-node-prev-sibling n))) + (string=3D "where" (treesit-node-type n)))) + + (lambda (_ b _) + (+ 1 (treesit-node-start (treesit-node-prev-sibling b)))) + 3) + ((parent-is "local_binds\\|instance_declarations") ,p-prev-sib 0) + + ;; Match + ((lambda (node _ _) + (and (string=3D "match" (treesit-node-type node)) + (string-match (regexp-opt '("patterns" "variable")) + (treesit-node-type (funcall ,p-n-prev node)))= )) + standalone-parent 2) + + ((node-is "match") ,p-prev-sib 0) + ((parent-is "match") standalone-parent 2) + + ((parent-is "^haskell$") column-0 0) + ((parent-is "^declarations$") column-0 0) + + ((parent-is "^record$") standalone-parent 2) + + ((parent-is "^exports$") + (lambda (_ b _) (treesit-node-start (treesit-node-prev-sibling b))) + 0) + ((n-p-gp nil "signature" "foreign_import") grand-parent 3) + ((parent-is "^case$") standalone-parent 4) + ((node-is "^alternatives$") + (lambda (_ b _) + (treesit-node-start (treesit-node-child b 0))) + 2) + ((node-is "^comment$") + (lambda (node parent _) + (pcase node + ;; (relevent means type not it haskell-ts--ignore-types) + ;; 1. next relevent sibling if exists + ((app ,p-sib (and (pred (not null)) n)) + (treesit-node-start n)) + ;; 2. previous relevent sibling if exists + ((app ,p-prev-sib (and (pred (not null)) n)) + n) + ;; 3. parent + (_ (treesit-node-start parent)))) + 0) + + ((node-is "|") parent 1) +=20=20=20=20=20=20=20 + ;; Backup + (catch-all parent 2)))) + "\"Simple\" treesit indentation rules for haskell.") + +(defvar haskell-ts-mode-syntax-table + (eval-when-compile + (let ((table (make-syntax-table)) + (syntax-list + `((" " " \t\n\r\f\v") + ("_" "!#$%&*+./<=3D>?\\^|-~:") + ("w" ?_ ?\') + ("." ",:@") + ("\"" ?\") + ("()" ?\() + (")(" ?\)) + ("(]" ?\[) + (")[" ?\]) + ("$`" ?\`) + ("(}1nb" ?\{ ) + ("){4nb" ?\} ) + ("_ 123" ?- ) + (">" "\r\n\f\v")))) + (dolist (ls syntax-list table) + (dolist (char (if (stringp (cadr ls)) + (string-to-list (cadr ls)) + (cdr ls))) + (modify-syntax-entry char (car ls) table))))) + "The syntax table for haskell.") + +(defun haskell-ts-sexp (node) + "Returns non-nil on a sexp node." + (let ((node-text (treesit-node-text node 1))) + (and + (not (member node-text '( "{" "}" "[" "]" "(" ")" ";"))) + (not (and (string=3D "operator" (treesit-node-field-name node)) + (=3D 1 (length node-text))))))) + +(defvar haskell-ts-thing-settings + `((haskell + (sexp haskell-ts-sexp) + (sentence "match") + (string "string") + (text "string"))) + "`treesit-thing-settings' for `haskell-ts-mode'.") + +(defmacro haskell-ts-imenu-name-function (check-func) + `(lambda (node) + (let ((nn (treesit-node-child node 0 node))) + (if (funcall ,check-func node) + (if (string=3D (treesit-node-type nn) "infix") + (treesit-node-text (treesit-node-child nn 1)) + (haskell-ts-defun-name node)) + nil)))) + +(defvar-keymap haskell-ts-mode-map + :doc "Keymap for haskell-ts-mode." + "C-c C-c" #'haskell-ts-compile-region-and-go + "C-c C-r" #'run-haskell) + +;;;###autoload +(define-derived-mode haskell-ts-mode prog-mode "haskell ts mode" + "Major mode for Haskell files using tree-sitter." + :table haskell-ts-mode-syntax-table + (unless (treesit-ready-p 'haskell) + (error "Tree-sitter for Haskell is not available")) + (setq treesit-primary-parser (treesit-parser-create 'haskell)) + (setq treesit-language-at-point-function + (lambda (&rest _) 'haskell)) + (setq-local treesit-defun-type-regexp "\\(?:\\(?:function\\|struct\\)_de= finition\\)") + ;; Indent + (when haskell-ts-use-indent + (setq-local treesit-simple-indent-rules haskell-ts-indent-rules) + (setq-local indent-tabs-mode nil)) + ;; Comment + (setq-local comment-start "-- ") + (setq-local comment-use-syntax t) + (setq-local comment-start-skip "\\(?: \\|^\\)-+") + ;; Electric + (setq-local electric-pair-pairs + '((?` . ?`) (?\( . ?\)) (?{ . ?}) (?\" . ?\") (?\[ . ?\]))) + ;; Navigation + (setq-local treesit-defun-name-function 'haskell-ts-defun-name) + (setq-local treesit-thing-settings haskell-ts-thing-settings) + (setq-local treesit-defun-type-regexp + ;; Since haskell is strict functional, any 2nd level + ;; entity is defintion + (cons ".+" + (lambda (node) + (and (not (string-match haskell-ts--ignore-types (tr= eesit-node-type node))) + (string=3D "declarations" (treesit-node-type (t= reesit-node-parent node))))))) + (setq-local prettify-symbols-alist + (append haskell-ts-prettify-symbols-alist + (and haskell-ts-prettify-words + haskell-ts-prettify-words-alist))) + + ;; Imenu + (setq-local treesit-simple-imenu-settings + `((nil haskell-ts-imenu-func-node-p nil + ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-f= unc-node-p)) + ("Signatures.." haskell-ts-imenu-sig-node-p nil + ,(haskell-ts-imenu-name-function #'haskell-ts-imenu-sig-n= ode-p)) + ("Data..." haskell-ts-imenu-data-type-p nil + (lambda (node) + (treesit-node-text (treesit-node-child node 1)))))) + ;; font-lock + (setq-local treesit-font-lock-level haskell-ts-font-lock-level) + (setq-local treesit-font-lock-settings haskell-ts-font-lock) + (setq-local treesit-font-lock-feature-list + haskell-ts-font-lock-feature-list) + (treesit-major-mode-setup)) + +(defun haskell-ts--fontify-func (node face) + (if (string=3D "variable" (treesit-node-type node)) + (put-text-property + (treesit-node-start node) + (treesit-node-end node) + 'face face) + (mapc (lambda (n) (haskell-ts--fontify-func n face)) + (treesit-node-children node)))) + +(defun haskell-ts--fontify-arg (node &optional _ _ _) + (haskell-ts--fontify-func node 'font-lock-variable-name-face)) + +(defun haskell-ts--fontify-params (node &optional _ _ _) + (haskell-ts--fontify-func node 'font-lock-function-name-face)) + +(defun haskell-ts--fontify-type (node &optional _ _ _) + (let ((last-child (treesit-node-child node -1))) + (if (string=3D (treesit-node-type last-child) "function") + (haskell-ts--fontify-type last-child) + (put-text-property + (treesit-node-start last-child) + (treesit-node-end last-child) + 'face 'font-lock-variable-name-face)))) + +(defun haskell-ts-imenu-node-p (regex node) + (and (string-match-p regex (treesit-node-type node)) + (string=3D (treesit-node-type (treesit-node-parent node)) "declarat= ions"))) + +(defun haskell-ts-imenu-func-node-p (node) + (haskell-ts-imenu-node-p "function\\|bind" node)) + +(defun haskell-ts-imenu-sig-node-p (node) + (haskell-ts-imenu-node-p "signature" node)) + +(defun haskell-ts-imenu-data-type-p (node) + (haskell-ts-imenu-node-p "data_type" node)) + +(defun haskell-ts-defun-name (node) + (treesit-node-text (treesit-node-child node 0))) + +(defun haskell-ts-compile-region-and-go () + "Compile the text from START to END in the haskell proc. +If region is not active, reload the whole file." + (interactive) + (let ((hs (haskell-ts-haskell-session))) + (if (region-active-p) + (let ((str (buffer-substring-no-properties + (region-beginning) + (region-end)))) + (comint-send-string hs ":{\n") + (comint-send-string + hs + ;; Things that may cause problem to ghci need to be + ;; escaped. TODO examine if other lines that start with + ;; colons might cause problems + (replace-regexp-in-string "^:\\}" "\\:}" str nil t)) + (comint-send-string hs "\n:}\n")) + (comint-send-string hs ":r\n")))) + +;;;###autoload +(defun run-haskell () + "Run an inferior Haskell process." + (interactive) + (let ((buffer (concat "*" haskell-ts-ghci-buffer-name "*"))) + (pop-to-buffer-same-window + (if (comint-check-proc buffer) + buffer + (make-comint haskell-ts-ghci-buffer-name haskell-ts-ghci nil buffer= -file-name))))) + +(defun haskell-ts-haskell-session () + (get-buffer-process (concat "*" haskell-ts-ghci-buffer-name "*"))) + +(when (treesit-ready-p 'haskell) + (add-to-list 'auto-mode-alist '("\\.hs\\'" . haskell-ts-mode))) + +(provide 'haskell-ts-mode) + +;; derive from `haskell-mode' on emacs v30+ +(when (functionp 'derived-mode-add-parents) + (derived-mode-add-parents 'haskell-ts-mode '(haskell-mode))) + +;;; haskell-ts-mode.el ends here --=-=-=--