diff options
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180913.348/haskell-cabal.el')
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180913.348/haskell-cabal.el | 1179 |
1 files changed, 0 insertions, 1179 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180913.348/haskell-cabal.el b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180913.348/haskell-cabal.el deleted file mode 100644 index 5fd8ea47eeea..000000000000 --- a/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180913.348/haskell-cabal.el +++ /dev/null @@ -1,1179 +0,0 @@ -;;; haskell-cabal.el --- Support for Cabal packages -*- lexical-binding: t -*- - -;; Copyright © 2007, 2008 Stefan Monnier -;; 2016 Arthur Fayzrakhmanov - -;; Author: Stefan Monnier <monnier@iro.umontreal.ca> - -;; This file is not part of GNU Emacs. - -;; This file 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, or (at your option) -;; any later version. - -;; This file 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 GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. - -;;; Commentary: - -;; Todo: - -;; - distinguish continued lines from indented lines. -;; - indent-line-function. -;; - outline-minor-mode. - -;;; Code: - -;; (defun haskell-cabal-extract-fields-from-doc () -;; (require 'xml) -;; (let ((section (completing-read -;; "Section: " -;; '("general-fields" "library" "executable" "buildinfo")))) -;; (goto-char (point-min)) -;; (search-forward (concat "<sect3 id=\"" section "\">"))) -;; (let* ((xml (xml-parse-region -;; (progn (search-forward "<variablelist>") (match-beginning 0)) -;; (progn (search-forward "</variablelist>") (point)))) -;; (varlist (cl-remove-if-not 'consp (cl-cddar xml))) -;; (syms (mapcar (lambda (entry) (cl-caddr (assq 'literal (assq 'term entry)))) -;; varlist)) -;; (fields (mapcar (lambda (sym) (substring-no-properties sym 0 -1)) syms))) -;; fields)) - -(require 'cl-lib) -(require 'haskell-utils) - -(defcustom haskell-hasktags-path "hasktags" - "Path to `hasktags' executable." - :group 'haskell - :type 'string) - -(defcustom haskell-hasktags-arguments '("-e" "-x") - "Additional arguments for `hasktags' executable. -By default these are: - --e - generate ETAGS file --x - generate additional information in CTAGS file." - :group 'haskell - :type '(list string)) - -(defconst haskell-cabal-general-fields - ;; Extracted with (haskell-cabal-extract-fields-from-doc "general-fields") - '("name" "version" "cabal-version" "license" "license-file" "copyright" - "author" "maintainer" "stability" "homepage" "package-url" "synopsis" - "description" "category" "tested-with" "build-depends" "data-files" - "extra-source-files" "extra-tmp-files")) - -(defconst haskell-cabal-library-fields - ;; Extracted with (haskell-cabal-extract-fields-from-doc "library") - '("exposed-modules")) - -(defconst haskell-cabal-executable-fields - ;; Extracted with (haskell-cabal-extract-fields-from-doc "executable") - '("executable" "main-is")) - -(defconst haskell-cabal-buildinfo-fields - ;; Extracted with (haskell-cabal-extract-fields-from-doc "buildinfo") - '("buildable" "other-modules" "hs-source-dirs" "extensions" "ghc-options" - "ghc-prof-options" "hugs-options" "nhc-options" "includes" - "install-includes" "include-dirs" "c-sources" "extra-libraries" - "extra-lib-dirs" "cc-options" "ld-options" "frameworks")) - -(defvar haskell-cabal-mode-syntax-table - (let ((st (make-syntax-table))) - ;; The comment syntax can't be described simply in syntax-table. - ;; We could use font-lock-syntactic-keywords, but is it worth it? - ;; (modify-syntax-entry ?- ". 12" st) - (modify-syntax-entry ?\n ">" st) - (modify-syntax-entry ?- "w" st) - st)) - -(defvar haskell-cabal-font-lock-keywords - ;; The comment syntax can't be described simply in syntax-table. - ;; We could use font-lock-syntactic-keywords, but is it worth it? - '(("^[ \t]*--.*" . font-lock-comment-face) - ("^ *\\([^ \t:]+\\):" (1 font-lock-keyword-face)) - ("^\\(Library\\)[ \t]*\\({\\|$\\)" (1 font-lock-keyword-face)) - ("^\\(Executable\\|Test-Suite\\|Benchmark\\)[ \t]+\\([^\n \t]*\\)" - (1 font-lock-keyword-face) (2 font-lock-function-name-face)) - ("^\\(Flag\\)[ \t]+\\([^\n \t]*\\)" - (1 font-lock-keyword-face) (2 font-lock-constant-face)) - ("^\\(Source-Repository\\)[ \t]+\\(head\\|this\\)" - (1 font-lock-keyword-face) (2 font-lock-constant-face)) - ("^ *\\(if\\)[ \t]+.*\\({\\|$\\)" (1 font-lock-keyword-face)) - ("^ *\\(}[ \t]*\\)?\\(else\\)[ \t]*\\({\\|$\\)" - (2 font-lock-keyword-face)) - ("\\<\\(?:True\\|False\\)\\>" - (0 font-lock-constant-face)))) - -(defvar haskell-cabal-buffers nil - "List of Cabal buffers.") - -(defun haskell-cabal-buffers-clean (&optional buffer) - "Refresh list of known cabal buffers. - -Check each buffer in variable `haskell-cabal-buffers' and remove -it from list if one of the following conditions are hold: -+ buffer is killed; -+ buffer's mode is not derived from `haskell-cabal-mode'; -+ buffer is a BUFFER (if given)." - (let ((bufs ())) - (dolist (buf haskell-cabal-buffers) - (if (and (buffer-live-p buf) - (not (eq buf buffer)) - (with-current-buffer buf (derived-mode-p 'haskell-cabal-mode))) - (push buf bufs))) - (setq haskell-cabal-buffers bufs))) - -(defun haskell-cabal-unregister-buffer () - "Exclude current buffer from global list of known cabal buffers." - (haskell-cabal-buffers-clean (current-buffer))) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.cabal\\'" . haskell-cabal-mode)) - -(defvar haskell-cabal-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-s") 'haskell-cabal-subsection-arrange-lines) - (define-key map (kbd "C-M-n") 'haskell-cabal-next-section) - (define-key map (kbd "C-M-p") 'haskell-cabal-previous-section) - (define-key map (kbd "M-n") 'haskell-cabal-next-subsection) - (define-key map (kbd "M-p") 'haskell-cabal-previous-subsection) - (define-key map (kbd "C-<down>") 'haskell-cabal-next-subsection) - (define-key map (kbd "C-<up>") 'haskell-cabal-previous-subsection) - (define-key map (kbd "C-c C-f") 'haskell-cabal-find-or-create-source-file) - (define-key map (kbd "M-g l") 'haskell-cabal-goto-library-section) - (define-key map (kbd "M-g e") 'haskell-cabal-goto-executable-section) - (define-key map (kbd "M-g b") 'haskell-cabal-goto-benchmark-section) - (define-key map (kbd "M-g t") 'haskell-cabal-goto-test-suite-section) - map)) - -;;;###autoload -(define-derived-mode haskell-cabal-mode fundamental-mode "Haskell-Cabal" - "Major mode for Cabal package description files." - (setq-local font-lock-defaults - '(haskell-cabal-font-lock-keywords t t nil nil)) - (add-to-list 'haskell-cabal-buffers (current-buffer)) - (add-hook 'change-major-mode-hook 'haskell-cabal-unregister-buffer nil 'local) - (add-hook 'kill-buffer-hook 'haskell-cabal-unregister-buffer nil 'local) - (setq-local comment-start "-- ") - (setq-local comment-start-skip "\\(^[ \t]*\\)--[ \t]*") - (setq-local comment-end "") - (setq-local comment-end-skip "[ \t]*\\(\\s>\\|\n\\)") - (setq-local indent-line-function 'haskell-cabal-indent-line) - (setq indent-tabs-mode nil) - ) - -(make-obsolete 'haskell-cabal-get-setting - 'haskell-cabal--get-field - "March 14, 2016") -(defalias 'haskell-cabal-get-setting 'haskell-cabal--get-field - "Try to read value of field with NAME from current buffer. -Obsolete function. Defined for backward compatibility. Use -`haskell-cabal--get-field' instead.") - -(defun haskell-cabal--get-field (name) - "Try to read value of field with NAME from current buffer." - (save-excursion - (let ((case-fold-search t)) - (goto-char (point-min)) - (when (re-search-forward - (concat "^[ \t]*" (regexp-quote name) - ":[ \t]*\\(.*\\(\n[ \t]+[ \t\n].*\\)*\\)") - nil t) - (let ((val (match-string 1)) - (start 1)) - (when (match-end 2) ;Multiple lines. - ;; The documentation is not very precise about what to do about - ;; the \n and the indentation: are they part of the value or - ;; the encoding? I take the point of view that \n is part of - ;; the value (so that values can span multiple lines as well), - ;; and that only the first char in the indentation is part of - ;; the encoding, the rest is part of the value (otherwise, lines - ;; in the value cannot start with spaces or tabs). - (while (string-match "^[ \t]\\(?:\\.$\\)?" val start) - (setq start (1+ (match-beginning 0))) - (setq val (replace-match "" t t val)))) - val))))) - - -(make-obsolete 'haskell-cabal-guess-setting - 'haskell-cabal-get-field - "March 14, 2016") -(defalias 'haskell-cabal-guess-setting 'haskell-cabal-get-field - "Read the value of field with NAME from project's cabal file. -Obsolete function. Defined for backward compatibility. Use -`haskell-cabal-get-field' instead.") - -;;;###autoload -(defun haskell-cabal-get-field (name) - "Read the value of field with NAME from project's cabal file. -If there is no valid .cabal file to get the setting from (or -there is no corresponding setting with that name in the .cabal -file), then this function returns nil." - (interactive) - (when (and name buffer-file-name) - (let ((cabal-file (haskell-cabal-find-file))) - (when (and cabal-file (file-readable-p cabal-file)) - (with-temp-buffer - (insert-file-contents cabal-file) - (haskell-cabal--get-field name)))))) - -;;;###autoload -(defun haskell-cabal-get-dir (&optional use-defaults) - "Get the Cabal dir for a new project. Various ways of figuring this out, - and indeed just prompting the user. Do them all." - (let* ((file (haskell-cabal-find-file)) - (dir (if file (file-name-directory file) default-directory))) - (if use-defaults - dir - (haskell-utils-read-directory-name - (format "Cabal dir%s: " (if file (format " (guessed from %s)" (file-relative-name file)) "")) - dir)))) - -(defun haskell-cabal-compute-checksum (dir) - "Compute MD5 checksum of package description file in DIR. -Return nil if no Cabal description file could be located via -`haskell-cabal-find-pkg-desc'." - (let ((cabal-file (haskell-cabal-find-pkg-desc dir))) - (when cabal-file - (with-temp-buffer - (insert-file-contents cabal-file) - (md5 (buffer-string)))))) - -(defun haskell-cabal-find-file (&optional dir) - "Search for package description file upwards starting from DIR. -If DIR is nil, `default-directory' is used as starting point for -directory traversal. Upward traversal is aborted if file owner -changes. Uses `haskell-cabal-find-pkg-desc' internally." - (let ((use-dir (or dir default-directory))) - (while (and use-dir (not (file-directory-p use-dir))) - (setq use-dir (file-name-directory (directory-file-name use-dir)))) - (when use-dir - (catch 'found - (let ((user (nth 2 (file-attributes use-dir))) - ;; Abbreviate, so as to stop when we cross ~/. - (root (abbreviate-file-name use-dir))) - ;; traverse current dir up to root as long as file owner doesn't change - (while (and root (equal user (nth 2 (file-attributes root)))) - (let ((cabal-file (haskell-cabal-find-pkg-desc root))) - (when cabal-file - (throw 'found cabal-file))) - - (let ((proot (file-name-directory (directory-file-name root)))) - (if (equal proot root) ;; fix-point reached? - (throw 'found nil) - (setq root proot)))) - nil))))) - -(defun haskell-cabal-find-pkg-desc (dir &optional allow-multiple) - "Find a package description file in the directory DIR. -Returns nil if none or multiple \".cabal\" files were found. If -ALLOW-MULTIPLE is non nil, in case of multiple \".cabal\" files, -a list is returned instead of failing with a nil result." - ;; This is basically a port of Cabal's - ;; Distribution.Simple.Utils.findPackageDesc function - ;; http://hackage.haskell.org/packages/archive/Cabal/1.16.0.3/doc/html/Distribution-Simple-Utils.html - ;; but without the exception throwing. - (let* ((cabal-files - (cl-remove-if 'file-directory-p - (cl-remove-if-not 'file-exists-p - (directory-files dir t ".\\.cabal\\'"))))) - (cond - ((= (length cabal-files) 1) (car cabal-files)) ;; exactly one candidate found - (allow-multiple cabal-files) ;; pass-thru multiple candidates - (t nil)))) - -(defun haskell-cabal-find-dir (&optional dir) - "Like `haskell-cabal-find-file' but returns directory instead. -See `haskell-cabal-find-file' for meaning of DIR argument." - (let ((cabal-file (haskell-cabal-find-file dir))) - (when cabal-file - (file-name-directory cabal-file)))) - -;;;###autoload -(defun haskell-cabal-visit-file (other-window) - "Locate and visit package description file for file visited by current buffer. -This uses `haskell-cabal-find-file' to locate the closest -\".cabal\" file and open it. This command assumes a common Cabal -project structure where the \".cabal\" file is in the top-folder -of the project, and all files related to the project are in or -below the top-folder. If called with non-nil prefix argument -OTHER-WINDOW use `find-file-other-window'." - (interactive "P") - ;; Note: We aren't allowed to rely on haskell-session here (which, - ;; in pathological cases, can have a different .cabal file - ;; associated with the current buffer) - (if buffer-file-name - (let ((cabal-file (haskell-cabal-find-file (file-name-directory buffer-file-name)))) - (if cabal-file - (if other-window - (find-file-other-window cabal-file) - (find-file cabal-file)) - (error "Could not locate \".cabal\" file for %S" buffer-file-name))) - (error "Cannot locate \".cabal\" file for buffers not visiting any file"))) - -(defvar haskell-cabal-commands - '("install" - "update" - "list" - "info" - "upgrade" - "fetch" - "unpack" - "check" - "sdist" - "upload" - "report" - "init" - "configure" - "build" - "copy" - "haddock" - "clean" - "hscolour" - "register" - "test" - "help" - "run")) - -;;;###autoload -(defgroup haskell-cabal nil - "Haskell cabal files" - :group 'haskell -) - -(defconst haskell-cabal-section-header-regexp "^[[:alnum:]]" ) -(defconst haskell-cabal-subsection-header-regexp "^[ \t]*[[:alnum:]]\\w*:") -(defconst haskell-cabal-comment-regexp "^[ \t]*--") -(defconst haskell-cabal-empty-regexp "^[ \t]*$") -(defconst haskell-cabal-conditional-regexp "^[ \t]*\\(\\if\\|else\\|}\\)") - -(defun haskell-cabal-classify-line () - "Classify the current line into 'section-header 'subsection-header 'section-data 'comment and 'empty '" - (save-excursion - (beginning-of-line) - (cond - ((looking-at haskell-cabal-subsection-header-regexp ) 'subsection-header) - ((looking-at haskell-cabal-section-header-regexp) 'section-header) - ((looking-at haskell-cabal-comment-regexp) 'comment) - ((looking-at haskell-cabal-empty-regexp ) 'empty) - ((looking-at haskell-cabal-conditional-regexp ) 'conditional) - (t 'section-data)))) - -(defun haskell-cabal-header-p () - "Is the current line a section or subsection header?" - (cl-case (haskell-cabal-classify-line) - ((section-header subsection-header) t))) - -(defun haskell-cabal-section-header-p () - "Is the current line a section or subsection header?" - (cl-case (haskell-cabal-classify-line) - ((section-header) t))) - - -(defun haskell-cabal-section-beginning () - "Find the beginning of the current section" - (save-excursion - (while (not (or (bobp) (haskell-cabal-section-header-p))) - (forward-line -1)) - (point))) - -(defun haskell-cabal-beginning-of-section () - "go to the beginning of the section" - (interactive) - (goto-char (haskell-cabal-section-beginning)) -) - -(defun haskell-cabal-section-end () - "Find the end of the current section" - (interactive) - (save-excursion - (if (re-search-forward "\n\\([ \t]*\n\\)*[[:alnum:]]" nil t) - (match-beginning 0) - (point-max)))) - -(defun haskell-cabal-end-of-section () - "go to the end of the section" - (interactive) - (goto-char (haskell-cabal-section-end))) - -(defun haskell-cabal-next-section () - "Go to the next section" - (interactive) - (when (haskell-cabal-section-header-p) (forward-line)) - (while (not (or (eobp) (haskell-cabal-section-header-p))) - (forward-line))) - -(defun haskell-cabal-previous-section () - "Go to the next section" - (interactive) - (when (haskell-cabal-section-header-p) (forward-line -1)) - (while (not (or (bobp) (haskell-cabal-section-header-p))) - (forward-line -1))) - -(defun haskell-cabal-subsection-end () - "find the end of the current subsection" - (save-excursion - (haskell-cabal-beginning-of-subsection) - (forward-line) - (while (and (not (eobp)) - (member (haskell-cabal-classify-line) '(empty section-data))) - (forward-line)) - (unless (eobp) (forward-line -1)) - (while (and (equal (haskell-cabal-classify-line) 'empty) - (not (bobp))) - (forward-line -1)) - (end-of-line) - (point))) - -(defun haskell-cabal-end-of-subsection () - "go to the end of the current subsection" - (interactive) - (goto-char (haskell-cabal-subsection-end))) - -(defun haskell-cabal-section () - "Get the name and data of the associated section" - (save-excursion - (haskell-cabal-beginning-of-section) - (when (and (haskell-cabal-section-header-p) - (looking-at "^\\(\\w+\\)[ \t]*\\(.*\\)$")) - (list :name (match-string-no-properties 1) - :value (match-string-no-properties 2) - :beginning (match-beginning 0) - :end (haskell-cabal-section-end))))) - - -(defun haskell-cabal-subsection () - "Get the name and bounds of of the current subsection" - (save-excursion - (haskell-cabal-beginning-of-subsection) - (when (looking-at "\\([ \t]*\\(\\w*\\):\\)[ \t]*") - (list :name (match-string-no-properties 2) - :beginning (match-end 0) - :end (save-match-data (haskell-cabal-subsection-end)) - :data-start-column (save-excursion (goto-char (match-end 0)) - (current-column) - ))))) - - -(defun haskell-cabal-section-name (section) - (plist-get section :name)) - -(defun haskell-cabal-section-value (section) - (plist-get section :value)) - -(defun haskell-cabal-section-start (section) - (plist-get section :beginning)) - -(defun haskell-cabal-section-data-start-column (section) - (plist-get section :data-start-column)) - -(defun haskell-cabal-map-component-type (component-type) - "Map from cabal file COMPONENT-TYPE to build command component-type." - (let ((component-type (downcase component-type))) - (cond ((equal component-type "executable") "exe") - ((equal component-type "test-suite") "test") - ((equal component-type "benchmark") "bench")))) - -(defun haskell-cabal-enum-targets (&optional process-type) - "Enumerate .cabal targets. PROCESS-TYPE determines the format of the returned target." - (let ((cabal-file (haskell-cabal-find-file)) - (process-type (if process-type process-type 'ghci))) - (when (and cabal-file (file-readable-p cabal-file)) - (with-temp-buffer - (insert-file-contents cabal-file) - (haskell-cabal-mode) - (goto-char (point-min)) - (let ((matches) - (package-name (haskell-cabal--get-field "name"))) - (haskell-cabal-next-section) - (while (not (eobp)) - (if (haskell-cabal-source-section-p (haskell-cabal-section)) - (let* ((section (haskell-cabal-section)) - (component-type (haskell-cabal-section-name section)) - (val (car (split-string - (haskell-cabal-section-value section))))) - (if (equal (downcase component-type) "library") - (let ((lib-target (if (eq 'stack-ghci process-type) - (concat package-name ":lib") - (concat "lib:" package-name)))) - (push lib-target matches)) - (push (concat (when (eq 'stack-ghci process-type) - (concat package-name ":")) - (haskell-cabal-map-component-type component-type) - ":" - val) - matches)))) - (haskell-cabal-next-section)) - (reverse matches)))))) - -(defmacro haskell-cabal-with-subsection (subsection replace &rest funs) - "Copy subsection data into a temporary buffer, save indentation -and execute FORMS - -If REPLACE is non-nil the subsection data is replaced with the -resulting buffer-content" - (let ((section (make-symbol "section")) - (beg (make-symbol "beg")) - (end (make-symbol "end")) - (start-col (make-symbol "start-col")) - (section-data (make-symbol "section-data"))) - `(let* ((,section ,subsection) - (,beg (plist-get ,section :beginning)) - (,end (plist-get ,section :end)) - (,start-col (plist-get ,section :data-start-column)) - (,section-data (buffer-substring ,beg ,end))) - (save-excursion - (prog1 - (with-temp-buffer - (setq indent-tabs-mode nil) - (indent-to ,start-col) - (insert ,section-data) - (goto-char (point-min)) - (prog1 - (progn (haskell-cabal-save-indentation ,@funs)) - (goto-char (point-min)) - (when (looking-at (format "[ ]\\{0,%d\\}" (1+ ,start-col))) - (replace-match "")) - - (setq ,section-data (buffer-substring (point-min) (point-max))))) - ,@(when replace - `((delete-region ,beg ,end) - (goto-char ,beg) - (insert ,section-data)))))))) - -(defmacro haskell-cabal-each-line (&rest fun) - "Execute FORMS on each line" - `(save-excursion - (while (< (point) (point-max)) - ,@fun - (forward-line)))) - -(defun haskell-cabal-chomp-line () - "Remove leading and trailing whitespaces from current line" - (beginning-of-line) - (when (looking-at "^[ \t]*\\([^ \t]\\|\\(?:[^ \t].*[^ \t]\\)\\)[ \t]*$") - (replace-match (match-string 1) nil t) - t)) - - -(defun haskell-cabal-min-indentation (&optional beg end) - "Compute largest common whitespace prefix of each line in between BEG and END" - (save-excursion - (goto-char (or beg (point-min))) - (let ((min-indent nil)) - (while (< (point) (or end (point-max))) - (let ((indent (current-indentation))) - (if (and (not (haskell-cabal-ignore-line-p)) - (or (not min-indent) - (< indent min-indent))) - (setq min-indent indent))) - (forward-line)) - min-indent))) - -(defun haskell-cabal-ignore-line-p () - "Does line only contain whitespaces and comments?" - (save-excursion - (beginning-of-line) - (looking-at "^[ \t]*\\(?:--.*\\)?$"))) - -(defun haskell-cabal-kill-indentation () - "Remove longest common whitespace prefix from each line" - (goto-char (point-min)) - (let ((indent (haskell-cabal-min-indentation))) - (haskell-cabal-each-line (unless (haskell-cabal-ignore-line-p) - (delete-char indent)) ) - indent)) - -(defun haskell-cabal-add-indentation (indent) - (goto-char (point-min)) - (haskell-cabal-each-line - (unless (haskell-cabal-ignore-line-p) - (indent-to indent)))) - - -(defmacro haskell-cabal-save-indentation (&rest funs) - "Strip indentation from each line, execute FORMS and reinstate indentation - so that the indentation of the FIRST LINE matches" - (let ((old-l1-indent (make-symbol "new-l1-indent")) - (new-l1-indent (make-symbol "old-l1-indent"))) - `(let ( (,old-l1-indent (save-excursion - (goto-char (point-min)) - (current-indentation)))) - (unwind-protect - (progn - (haskell-cabal-kill-indentation) - ,@funs) - (progn - (goto-char (point-min)) - (let ((,new-l1-indent (current-indentation))) - (haskell-cabal-add-indentation (- ,old-l1-indent - ,new-l1-indent)))))))) - -(defun haskell-cabal-comma-separatorp (pos) - "Return non-nil when the char at POS is a comma separator. -Characters that are not a comma, or commas inside a commment or -string, are not comma separators." - (when (eq (char-after pos) ?,) - (let ((ss (syntax-ppss pos))) - (not - (or - ;; inside a string - (nth 3 ss) - ;; inside a comment - (nth 4 ss)))))) - -(defun haskell-cabal-strip-list-and-detect-style () - "Strip commas from a comma-separated list. -Detect and return the comma style. The possible options are: - -before: a comma at the start of each line (except the first), e.g. - Foo - , Bar - -after: a comma at the end of each line (except the last), e.g. - Foo, - Bar - -single: everything on a single line, but comma-separated, e.g. - Foo, Bar - -nil: no commas, e.g. - Foo Bar - -If the styles are mixed, the position of the first comma -determines the style. If there is only one element then `after' -style is assumed." - (let (comma-style) - ;; split list items on single line - (goto-char (point-min)) - (while (re-search-forward - "\\([^ \t,\n]\\)[ \t]*\\(,\\)[ \t]*\\([^ \t,\n]\\)" nil t) - (when (haskell-cabal-comma-separatorp (match-beginning 2)) - (setq comma-style 'single) - (replace-match "\\1\n\\3" nil nil))) - ;; remove commas before - (goto-char (point-min)) - (while (re-search-forward "^\\([ \t]*\\),\\([ \t]*\\)" nil t) - (setq comma-style 'before) - (replace-match "" nil nil)) - ;; remove trailing commas - (goto-char (point-min)) - (while (re-search-forward ",[ \t]*$" nil t) - (unless (eq comma-style 'before) - (setq comma-style 'after)) - (replace-match "" nil nil)) - - ;; if there is just one line then set default as 'after - (unless comma-style - (goto-char (point-min)) - (forward-line) - (when (eobp) - (setq comma-style 'after))) - (goto-char (point-min)) - - (haskell-cabal-each-line (haskell-cabal-chomp-line)) - comma-style)) - -(defun haskell-cabal-listify (comma-style) - "Add commas so that the buffer contains a comma-separated list. -Respect the COMMA-STYLE, see -`haskell-cabal-strip-list-and-detect-style' for the possible -styles." - (cl-case comma-style - ('before - (goto-char (point-min)) - (while (haskell-cabal-ignore-line-p) (forward-line)) - (indent-to 2) - (forward-line) - (haskell-cabal-each-line - (unless (haskell-cabal-ignore-line-p) - (insert ", ")))) - ('after - (goto-char (point-max)) - (while (equal 0 (forward-line -1)) - (unless (haskell-cabal-ignore-line-p) - (end-of-line) - (insert ",") - (beginning-of-line)))) - ('single - (goto-char (point-min)) - (while (not (eobp)) - (end-of-line) - (unless (eobp) - (insert ", ") - (delete-char 1) - (just-one-space)))))) - -(defmacro haskell-cabal-with-cs-list (&rest funs) - "Format the buffer so that each line contains a list element. -Respect the comma style." - (let ((comma-style (make-symbol "comma-style"))) - `(let ((,comma-style - (save-excursion - (haskell-cabal-strip-list-and-detect-style)))) - (unwind-protect (progn ,@funs) - (haskell-cabal-listify ,comma-style))))) - - -(defun haskell-cabal-sort-lines-key-fun () - (when (looking-at "[ \t]*--[ \t,]*") - (goto-char (match-end 0))) - nil) - -(defmacro haskell-cabal-save-position (&rest forms) - "Save position as mark, execute FORMs and go back to mark" - `(prog2 - (haskell-cabal-mark) - (progn ,@forms) - (haskell-cabal-goto-mark) - (haskell-cabal-remove-mark))) - -(defun haskell-cabal-sort-lines-depends-compare (key1 key2) - (let* ((key1str (buffer-substring (car key1) (cdr key1))) - (key2str (buffer-substring (car key2) (cdr key2))) - (base-regex "^[ \t]*base\\($\\|[^[:alnum:]-]\\)")) - (cond - ((string-match base-regex key1str) t) - ((string-match base-regex key2str) nil) - (t (string< key1str key2str))))) - -(defun haskell-cabal-subsection-arrange-lines () - "Sort lines of current subsection" - (interactive) - (haskell-cabal-save-position - (let* ((subsection (haskell-cabal-section-name (haskell-cabal-subsection))) - (compare-lines (if (string= (downcase subsection) "build-depends") - 'haskell-cabal-sort-lines-depends-compare - nil))) - (haskell-cabal-with-subsection - (haskell-cabal-subsection) t - (haskell-cabal-with-cs-list - (sort-subr nil 'forward-line 'end-of-line - 'haskell-cabal-sort-lines-key-fun - 'end-of-line - compare-lines - )))))) - -(defun haskell-cabal-subsection-beginning () - "find the beginning of the current subsection" - (save-excursion - (while (and (not (bobp)) - (not (haskell-cabal-header-p))) - (forward-line -1)) - (back-to-indentation) - (point))) - -(defun haskell-cabal-beginning-of-subsection () - "go to the beginning of the current subsection" - (interactive) - (goto-char (haskell-cabal-subsection-beginning))) - -(defun haskell-cabal-next-subsection () - "go to the next subsection" - (interactive) - (if (haskell-cabal-header-p) (forward-line)) - (while (and (not (eobp)) - (not (haskell-cabal-header-p))) - (forward-line)) - (haskell-cabal-forward-to-line-entry)) - -(defun haskell-cabal-previous-subsection () - "go to the previous subsection" - (interactive) - (if (haskell-cabal-header-p) (forward-line -1)) - (while (and (not (bobp)) - (not (haskell-cabal-header-p))) - (forward-line -1)) - (haskell-cabal-forward-to-line-entry) - ) - - -(defun haskell-cabal-find-subsection-by (section pred) - "Find subsection with name NAME" - (save-excursion - (when section (goto-char (haskell-cabal-section-start section))) - (let* ((end (if section (haskell-cabal-section-end) (point-max))) - (found nil)) - (while (and (< (point) end) - (not found)) - (let ((subsection (haskell-cabal-subsection))) - (when (and subsection (funcall pred subsection)) - (setq found subsection))) - (haskell-cabal-next-subsection)) - found))) - -(defun haskell-cabal-find-subsection (section name) - "Find subsection with name NAME" - (let ((downcase-name (downcase name))) - (haskell-cabal-find-subsection-by - section - `(lambda (subsection) - (string= (downcase (haskell-cabal-section-name subsection)) - ,downcase-name))))) - -(defun haskell-cabal-goto-subsection (name) - (let ((subsection (haskell-cabal-find-subsection (haskell-cabal-section) name))) - (when subsection - (goto-char (haskell-cabal-section-start subsection))))) - -(defun haskell-cabal-goto-exposed-modules () - (interactive) - (haskell-cabal-goto-subsection "exposed-modules")) - -(defun haskell-cabal-subsection-entry-list (section name) - "Get the data of a subsection as a list" - (let ((subsection (haskell-cabal-find-subsection section name))) - (when subsection - (haskell-cabal-with-subsection - subsection nil - (haskell-cabal-with-cs-list - (delete-matching-lines - (format "\\(?:%s\\)\\|\\(?:%s\\)" - haskell-cabal-comment-regexp - haskell-cabal-empty-regexp) - (point-min) (point-max)) - (split-string (buffer-substring-no-properties (point-min) (point-max)) - "\n" t)))))) - -(defun haskell-cabal-remove-mark () - (remove-list-of-text-properties (point-min) (point-max) - '(haskell-cabal-marker))) - - -(defun haskell-cabal-mark () - "Mark the current position with the text property haskell-cabal-marker" - (haskell-cabal-remove-mark) - (put-text-property (line-beginning-position) (line-end-position) - 'haskell-cabal-marker 'marked-line) - (put-text-property (point) (1+ (point)) - 'haskell-cabal-marker 'marked)) - - -(defun haskell-cabal-goto-mark () - "Go to marked line" - (let ((marked-pos (text-property-any (point-min) (point-max) - 'haskell-cabal-marker - 'marked)) - (marked-line (text-property-any (point-min) (point-max) - 'haskell-cabal-marker - 'marked-line) ) - ) - (cond (marked-pos (goto-char marked-pos)) - (marked-line (goto-char marked-line))))) - -(defmacro haskell-cabal-with-subsection-line (replace &rest forms) - "Mark line, copy subsection data into a temporary buffer, save indentation -and execute FORMS at the marked line. - -If REPLACE is non-nil the subsection data is replaced with the -resulting buffer-content. Unmark line at the end." - `(progn - (haskell-cabal-mark) - (unwind-protect - (haskell-cabal-with-subsection (haskell-cabal-subsection) ,replace - (haskell-cabal-goto-mark) - ,@forms) - (haskell-cabal-remove-mark)))) - - -(defun haskell-cabal-get-line-content () - (haskell-cabal-with-subsection-line - nil - (haskell-cabal-with-cs-list - (haskell-cabal-goto-mark) - (buffer-substring-no-properties (line-beginning-position) - (line-end-position))))) - -(defun haskell-cabal-module-to-filename (module) - (concat (replace-regexp-in-string "[.]" "/" module ) ".hs")) - -(defconst haskell-cabal-module-sections '("exposed-modules" "other-modules") - "List of sections that contain module names" -) - -(defconst haskell-cabal-file-sections - '("main-is" "c-sources" "data-files" "extra-source-files" - "extra-doc-files" "extra-tmp-files" ) - "List of subsections that contain filenames" - ) - -(defconst haskell-cabal-source-bearing-sections - '("library" "executable" "test-suite" "benchmark")) - -(defun haskell-cabal-source-section-p (section) - (not (not (member (downcase (haskell-cabal-section-name section)) - haskell-cabal-source-bearing-sections)))) - -(defun haskell-cabal-line-filename () - "Expand filename in current line according to the subsection type - -Module names in exposed-modules and other-modules are expanded by replacing each dot (.) in the module name with a foward slash (/) and appending \".hs\" - -Example: Foo.Bar.Quux ==> Foo/Bar/Quux.hs - -Source names from main-is and c-sources sections are left untouched - -" - (let ((entry (haskell-cabal-get-line-content)) - (subsection (downcase (haskell-cabal-section-name - (haskell-cabal-subsection))))) - (cond ((member subsection haskell-cabal-module-sections) - (haskell-cabal-module-to-filename entry)) - ((member subsection haskell-cabal-file-sections) entry)))) - -(defun haskell-cabal-join-paths (&rest args) - "Crude hack to replace f-join" - (mapconcat 'identity args "/") -) - -(defun haskell-cabal-find-or-create-source-file () - "Open the source file this line refers to." - (interactive) - (let* ((src-dirs (append (haskell-cabal-subsection-entry-list - (haskell-cabal-section) "hs-source-dirs") - '(""))) - (base-dir (file-name-directory (buffer-file-name))) - (filename (haskell-cabal-line-filename))) - (when filename - (let ((candidates - (delq nil (mapcar - (lambda (dir) - (let ((file (haskell-cabal-join-paths base-dir - dir - filename))) - (when (and (file-readable-p file) - (not (file-directory-p file))) - file))) - src-dirs)))) - (if (null candidates) - (unwind-protect - (progn - (haskell-mode-toggle-interactive-prompt-state) - (let* ((src-dir - (haskell-cabal-join-paths base-dir - (or (car src-dirs) ""))) - (newfile (haskell-cabal-join-paths src-dir filename)) - (do-create-p (y-or-n-p (format "Create file %s ?" newfile)))) - (when do-create-p - (find-file-other-window newfile )))) - (haskell-mode-toggle-interactive-prompt-state t)) - (find-file-other-window (car candidates))))))) - - -(defun haskell-cabal-find-section-type (type &optional wrap) - (save-excursion - (haskell-cabal-next-section) - (while - (not - (or - (eobp) - (string= - (downcase type) - (downcase (haskell-cabal-section-name (haskell-cabal-section)))))) - (haskell-cabal-next-section)) - (if (eobp) - (if wrap (progn - (goto-char (point-min)) - (haskell-cabal-find-section-type type nil) ) - nil) - (point)))) - -(defun haskell-cabal-goto-section-type (type) - (let ((section (haskell-cabal-find-section-type type t))) - (if section (goto-char section) - (message "No %s section found" type)))) - -(defun haskell-cabal-goto-library-section () - (interactive) - (haskell-cabal-goto-section-type "library")) - -(defun haskell-cabal-goto-test-suite-section () - (interactive) - (haskell-cabal-goto-section-type "test-suite")) - -(defun haskell-cabal-goto-executable-section () - (interactive) - (haskell-cabal-goto-section-type "executable")) - -(defun haskell-cabal-goto-benchmark-section () - (interactive) - (haskell-cabal-goto-section-type "benchmark")) - - - -(defun haskell-cabal-line-entry-column () - "Column at which the line entry starts" - (save-excursion - (cl-case (haskell-cabal-classify-line) - (section-data (beginning-of-line) - (when (looking-at "[ ]*\\(?:,[ ]*\\)?") - (goto-char (match-end 0)) - (current-column))) - (subsection-header - (haskell-cabal-section-data-start-column (haskell-cabal-subsection)))))) - -(defun haskell-cabal-forward-to-line-entry () - "go forward to the beginning of the line entry (but never move backwards)" - (let ((col (haskell-cabal-line-entry-column))) - (when (and col (< (current-column) col)) - (beginning-of-line) - (forward-char col)))) - -(defun haskell-cabal-indent-line () - "Indent current line according to subsection" - (interactive) - (cl-case (haskell-cabal-classify-line) - (section-data - (save-excursion - (let ((indent (haskell-cabal-section-data-start-column - (haskell-cabal-subsection)))) - (indent-line-to indent) - (beginning-of-line) - (when (looking-at "[ ]*\\([ ]\\{2\\},[ ]*\\)") - (replace-match ", " t t nil 1))))) - (empty - (indent-relative))) - (haskell-cabal-forward-to-line-entry)) - -(defun haskell-cabal-map-sections (fun) - "Execute fun over each section, collecting the result" - (save-excursion - (goto-char (point-min)) - (let ((results nil)) - (while (not (eobp)) - (let* ((section (haskell-cabal-section)) - (result (and section (funcall fun (haskell-cabal-section))))) - (when section (setq results (cons result results)))) - (haskell-cabal-next-section)) - (nreverse results)))) - -(defun haskell-cabal-section-add-build-dependency (dependency &optional sort sec) - "Add a build dependency to the build-depends section" - (let* ((section (or sec (haskell-cabal-section))) - (subsection (and section - (haskell-cabal-find-subsection section "build-depends")))) - (when subsection - (haskell-cabal-with-subsection - subsection t - (haskell-cabal-with-cs-list - (insert dependency) - (insert "\n") - (when sort - (goto-char (point-min)) - (sort-subr nil 'forward-line 'end-of-line - 'haskell-cabal-sort-lines-key-fun))))))) - -(defun haskell-cabal-add-build-dependency (dependency &optional sort silent) - "Add the given DEPENDENCY to every section in cabal file. -If SORT argument is given sort dependencies in section after update. -Pass SILENT argument to update all sections without asking user." - (haskell-cabal-map-sections - (lambda (section) - (when (haskell-cabal-source-section-p section) - (unwind-protect - (progn - (when - (or silent - (y-or-n-p (format "Add dependency %s to %s section %s?" - dependency - (haskell-cabal-section-name section) - (haskell-cabal-section-value section)))) - (haskell-cabal-section-add-build-dependency dependency - sort - section)) - nil) - (haskell-mode-toggle-interactive-prompt-state t)))))) - -(defun haskell-cabal-add-dependency - (package &optional version no-prompt sort silent) - "Add PACKAGE to the cabal file. -If VERSION is non-nil it will be appended as a minimum version. -If NO-PROMPT is nil the minimum package version is read from the -minibuffer. When SORT is non-nil the package entries are sorted -afterwards. If SILENT is non-nil the user is prompted for each -source-section." - (interactive - (list (read-from-minibuffer "Package entry: ") nil t t nil)) - (haskell-mode-toggle-interactive-prompt-state) - (unwind-protect - (save-window-excursion - (find-file-other-window (haskell-cabal-find-file)) - (let ((entry (if no-prompt package - (read-from-minibuffer - "Package entry: " - (concat package - (if version (concat " >= " version) "")))))) - (haskell-cabal-add-build-dependency entry sort silent) - (when (or silent (y-or-n-p "Save cabal file? ")) - (save-buffer)))) - ;; unwind - (haskell-mode-toggle-interactive-prompt-state t))) - - -(defun haskell-cabal--find-tags-dir () - "Return a directory where TAGS file will be generated. -Tries to find cabal file first and if succeeds uses its location. -If cabal file not found uses current file directory. If current -buffer not visiting a file returns nil." - (or (haskell-cabal-find-dir) - (when buffer-file-name - (file-name-directory buffer-file-name)))) - -(defun haskell-cabal--compose-hasktags-command (dir) - "Prepare command to execute `hasktags` command in DIR folder. - -To customise the command executed, see `haskell-hasktags-path' -and `haskell-hasktags-arguments'. - -This function takes into account the user's operating system: in case -of Windows it generates a simple command, relying on Hasktags -itself to find source files: - -hasktags --output=DIR\TAGS -x -e DIR - -In other cases it uses `find` command to find all source files -recursively avoiding visiting unnecessary heavy directories like -.git, .svn, _darcs and build directories created by -cabal-install, stack, etc and passes list of found files to Hasktags." - (if (eq system-type 'windows-nt) - (format "%s --output=%s %s %s" - haskell-hasktags-path - (shell-quote-argument (expand-file-name "TAGS" dir)) - (mapconcat #'identity haskell-hasktags-arguments " ") - (shell-quote-argument dir)) - (format "cd %s && %s | %s" - (shell-quote-argument dir) - (concat "find . " - "-type d \\( " - "-name .git " - "-o -name .svn " - "-o -name _darcs " - "-o -name .stack-work " - "-o -name dist " - "-o -name dist-newstyle " - "-o -name .cabal-sandbox " - "\\) -prune " - "-o -type f \\( " - "-name '*.hs' " - "-or -name '*.lhs' " - "-or -name '*.hsc' " - "\\) -not \\( " - "-name '#*' " - "-or -name '.*' " - "\\) -print0") - (format "xargs -0 %s %s" - (shell-quote-argument haskell-hasktags-path) - (mapconcat #'identity haskell-hasktags-arguments " "))))) - -(provide 'haskell-cabal) -;;; haskell-cabal.el ends here |