├── .dir-locals.el ├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── COPYING ├── Makefile ├── NEWS ├── README.md ├── doc ├── anim │ ├── company-mode-import-statement.gif │ ├── company-mode-language-pragma.gif │ ├── flyspell-prog-mode.gif │ ├── font-lock-quasi-quotes.gif │ ├── font-lock-types.gif │ ├── font-lock.gif │ └── string-escape-highlight.gif ├── deploy-manual.sh ├── gifcasts │ ├── get-window-id.m │ └── haskell-gifcasts.el ├── haskell-manual-fixups.el ├── haskell-mode-travis-deploy-key ├── haskell-mode.css └── haskell-mode.texi ├── ghc-core.el ├── ghci-script-mode.el ├── haskell-align-imports.el ├── haskell-c2hs.el ├── haskell-cabal.el ├── haskell-collapse.el ├── haskell-commands.el ├── haskell-compile.el ├── haskell-complete-module.el ├── haskell-completions.el ├── haskell-customize.el ├── haskell-debug.el ├── haskell-decl-scan.el ├── haskell-doc.el ├── haskell-font-lock.el ├── haskell-ghc-support.el ├── haskell-hoogle.el ├── haskell-indent.el ├── haskell-indentation.el ├── haskell-interactive-mode.el ├── haskell-lexeme.el ├── haskell-load.el ├── haskell-menu.el ├── haskell-mode.el ├── haskell-modules.el ├── haskell-move-nested.el ├── haskell-navigate-imports.el ├── haskell-presentation-mode.el ├── haskell-process.el ├── haskell-repl.el ├── haskell-sandbox.el ├── haskell-session.el ├── haskell-sort-imports.el ├── haskell-string.el ├── haskell-svg.el ├── haskell-unicode-input-method.el ├── haskell-utils.el ├── haskell.el ├── highlight-uses-mode.el ├── images ├── haskell-mode-128x128.png ├── haskell-mode-256x256.png ├── haskell-mode-32x32.png ├── haskell-mode-512x512.png ├── haskell-mode-64x64.png ├── haskell-mode.svg └── star-history-700.png ├── inf-haskell.el ├── logo.svg ├── tests ├── haskell-c2hs-tests.el ├── haskell-cabal-tests.el ├── haskell-collapse-tests.el ├── haskell-completions-tests.el ├── haskell-customize-tests.el ├── haskell-decl-scan-tests.el ├── haskell-doc-tests.el ├── haskell-exec-tests.el ├── haskell-font-lock-tests.el ├── haskell-indent-tests.el ├── haskell-indentation-tests.el ├── haskell-lexeme-tests.el ├── haskell-load-tests.el ├── haskell-mode-tests.el ├── haskell-process-tests.el ├── haskell-sort-imports-tests.el ├── haskell-string-tests.el ├── haskell-test-utils-tests.el ├── haskell-test-utils.el ├── haskell-utils-tests.el ├── inferior-haskell-tests.el ├── interactive-haskell-mode-tests.el └── test-data │ └── Test.cabal └── w3m-haddock.el /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;;; Directory Local Variables 2 | ;;; For more information see (info "(emacs) Directory Variables") 3 | 4 | ((emacs-lisp-mode 5 | (indent-tabs-mode) 6 | (package-lint-main-file . "haskell-mode.el"))) 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | commit-message: 9 | prefix: "chore" 10 | include: "scope" 11 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: [master] 6 | push: 7 | branches: [master] 8 | paths-ignore: 9 | - '**.md' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | emacs_version: 17 | - 25.1 18 | - 25.3 19 | - 26.1 20 | - 26.3 21 | - 27.1 22 | - 27.2 23 | - 28.1 24 | - 28.2 25 | - 29.1 26 | - 29.4 27 | - snapshot 28 | include: 29 | - emacs_version: 29.4 30 | target: deploy-manual 31 | steps: 32 | - uses: cachix/install-nix-action@v31 33 | with: 34 | nix_path: nixpkgs=channel:nixos-unstable 35 | - uses: purcell/setup-emacs@master 36 | with: 37 | version: ${{ matrix.emacs_version }} 38 | 39 | - uses: actions/checkout@v4 40 | - name: Install stack and ghc 41 | run: nix profile install 'nixpkgs#stack' 'nixpkgs#ghc' 42 | - name: Install texinfo 43 | if: matrix.target == 'deploy-manual' 44 | run: nix profile install 'nixpkgs#texinfo' 45 | - name: Run make 46 | run: make ${{ matrix.target || 'check' }} 47 | continue-on-error: ${{ matrix.emacs_version == 'snapshot' && (! matrix.target) }} 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | *~ 3 | haskell-mode-autoloads.el 4 | haskell-mode.info 5 | haskell-mode.tmp.texi 6 | dir 7 | build-* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: nix 2 | 3 | env: 4 | - EMACS_CI=emacs-26-3 TARGET=deploy-manual 5 | 6 | install: | 7 | sh <(curl https://get.haskellstack.org/) 8 | bash <(curl https://raw.githubusercontent.com/purcell/nix-emacs-ci/master/travis-install) 9 | if [ "$TARGET" = "deploy-manual" ]; then 10 | nix-env -iA texinfo -f '' 11 | fi 12 | 13 | script: | 14 | make $TARGET 15 | 16 | notifications: 17 | email: false 18 | irc: "irc.freenode.org#haskell-emacs" 19 | 20 | # Local Variables: 21 | # indent-tabs-mode: nil 22 | # coding: utf-8 23 | # End: 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome nice stranger! 2 | 3 | Haskell Mode loves contributors and we strive to go extra length to 4 | help out both newcomers and serial contributors. 5 | 6 | Haskell Mode project is managed according to rules outlined in the wiki: 7 | 8 | https://github.com/haskell/haskell-mode/wiki/Contributing 9 | 10 | > When I started contributing to haskell-mode I had 0 experience in Elisp, but it was super easy to learn it. 11 | > -- [geraldus, 2016-01-16](https://github.com/haskell/haskell-mode/issues/1086#issuecomment-172177949) 12 | 13 | # Hacking on Haskell Mode 14 | 15 | To try out your changes to Haskell Mode without affecting your global 16 | Emacs installation, build Haskell Mode by running `make` here, and 17 | then run 18 | 19 | emacs -L 20 | 21 | from any directory you'd like to test in. This makes Emacs load 22 | Haskell Mode from this Git repo, instead of using any globally 23 | installed version you might have. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Note: Due to MELPA distributing directly from github source version 3 | # needs to be embedded in files as is without preprocessing. 4 | # 5 | # Version string is present in: 6 | # - Makefile 7 | # - haskell-mode.el 8 | # - haskell-mode.texi 9 | # 10 | # We should have a script that changes it everywhere it is needed and 11 | # syncs it with current git tag. 12 | # 13 | VERSION = 17.5 14 | 15 | INSTALL_INFO = install-info 16 | 17 | # Use $EMACS environment variable if present, so that all of these are 18 | # equivalent: 19 | # 20 | # 1. export EMACS=/path/to/emacs && make 21 | # 2. EMACS=/path/to/emacs make 22 | # 3. make EMACS=/path/to/emacs 23 | # 24 | # This is particularly useful when EMACS is set in ~/.bash_profile 25 | # 26 | EMACS ?= emacs 27 | EMACS_VERSION := $(shell "$(EMACS)" -Q --batch --eval '(princ emacs-version)') 28 | 29 | EFLAGS = --eval "(when (boundp 'load-prefer-newer) (setq load-prefer-newer t))" 30 | 31 | BATCH = @echo EMACS $@; "$(EMACS)" $(EFLAGS) --batch -Q -L . 32 | 33 | ELFILES := $(filter-out haskell-mode-autoloads.el,$(wildcard *.el)) 34 | 35 | ELCHECKS := $(wildcard tests/*-tests.el) 36 | 37 | AUTOLOADS = haskell-mode-autoloads.el 38 | 39 | PKG_DIST_FILES = $(ELFILES) logo.svg NEWS haskell-mode.info dir 40 | 41 | INIT_PACKAGES="(progn \ 42 | (require 'package) \ 43 | (push '(\"melpa\" . \"https://melpa.org/packages/\") package-archives) \ 44 | (package-initialize) \ 45 | (dolist (pkg '(package-lint)) \ 46 | (unless (package-installed-p pkg) \ 47 | (unless (assoc pkg package-archive-contents) \ 48 | (package-refresh-contents)) \ 49 | (package-install pkg))) \ 50 | )" 51 | 52 | 53 | .PHONY: all compile info clean check check-emacs-version check-ert check-package-lint check-relint 54 | 55 | all: check-emacs-version compile $(AUTOLOADS) info 56 | 57 | check-emacs-version : 58 | $(BATCH) --eval "(when (version< emacs-version \"25.1\") \ 59 | (message \"Error: haskell-mode requires Emacs 25.1 or later\") \ 60 | (message \"Your version of Emacs is %s\" emacs-version) \ 61 | (message \"Found as '$(EMACS)'\") \ 62 | (message \"Use one of:\") \ 63 | (message \" 1. export EMACS=/path/to/emacs && make\") \ 64 | (message \" 2. EMACS=/path/to/emacs make\") \ 65 | (message \" 3. make EMACS=/path/to/emacs\") \ 66 | (kill-emacs 2))" 67 | @echo Using EMACS = $(EMACS), version = $(EMACS_VERSION) 68 | 69 | compile: build-$(EMACS_VERSION)/build-flag 70 | 71 | build-$(EMACS_VERSION) : 72 | mkdir $@ 73 | 74 | # Emacs byte compilation state leaks from file to file if multiple 75 | # files are requested to be build at the same time. We have to 76 | # workaround this issue on Makefile level. Note also that we consider 77 | # an .el file to be dependent on all other files because we do not do 78 | # proper dependency tracking (yet). 79 | build-$(EMACS_VERSION)/%.elc : %.el $(ELFILES) 80 | $(BATCH) --eval '(setq byte-compile-error-on-warn t)' \ 81 | --eval "(setq byte-compile-dest-file-function (lambda (filename) \ 82 | (concat (file-name-directory filename) \"build-\" emacs-version \"/\" \ 83 | (file-name-nondirectory filename) \"c\")))" \ 84 | --eval "(when (check-declare-file \"$<\") (kill-emacs 2))" \ 85 | -f batch-byte-compile $< \ 86 | 87 | build-$(EMACS_VERSION)/build-flag : build-$(EMACS_VERSION) $(patsubst %.el,build-$(EMACS_VERSION)/%.elc,$(ELFILES)) 88 | touch $@ 89 | 90 | check-%: tests/%-tests.el 91 | $(BATCH) -l "$<" -f ert-run-tests-batch-and-exit; 92 | 93 | check: compile $(AUTOLOADS) check-package-lint check-relint check-ert 94 | 95 | check-ert: $(ELCHECKS) 96 | $(BATCH) -L tests \ 97 | $(patsubst %,-l %,$(ELCHECKS)) \ 98 | -f ert-run-tests-batch-and-exit 99 | @echo "checks passed!" 100 | 101 | # TODO: fix issues, then enforce build failure if this fails 102 | check-package-lint: 103 | $(BATCH) --eval $(INIT_PACKAGES) --eval '(setq package-lint-main-file "haskell-mode.el")' -f package-lint-batch-and-exit $(ELFILES) || true 104 | 105 | clean: 106 | $(RM) -r build-$(EMACS_VERSION) $(AUTOLOADS) $(AUTOLOADS:.el=.elc) haskell-mode.info dir 107 | 108 | info: haskell-mode.info dir 109 | 110 | dir: haskell-mode.info 111 | $(INSTALL_INFO) --dir=$@ $< 112 | 113 | haskell-mode.info: doc/haskell-mode.texi 114 | LANG=en_US.UTF-8 $(MAKEINFO) $(MAKEINFO_FLAGS) -o $@ $< 115 | 116 | doc/haskell-mode.html: doc/haskell-mode.texi doc/haskell-mode.css 117 | LANG=en_US.UTF-8 $(MAKEINFO) $(MAKEINFO_FLAGS) --html --css-include=doc/haskell-mode.css --no-split -o $@ $< 118 | $(BATCH) -l doc/haskell-manual-fixups.el -f haskell-manual-fixups-batch-and-exit $@ 119 | 120 | doc/html/index.html : doc/haskell-mode.texi 121 | if [ -e doc/html ]; then rm -r doc/html; fi 122 | mkdir doc/html 123 | cp -r doc/anim doc/html/anim 124 | LANG=en_US.UTF-8 $(MAKEINFO) $(MAKEINFO_FLAGS) --html \ 125 | --css-ref=haskell-mode.css \ 126 | -c AFTER_BODY_OPEN='
' \ 127 | -c EXTRA_HEAD='' \ 128 | -c SHOW_TITLE=0 \ 129 | -o doc/html $< 130 | $(BATCH) -l doc/haskell-manual-fixups.el -f haskell-manual-fixups-batch-and-exit doc/html/*.html 131 | 132 | doc/html/haskell-mode.css : doc/haskell-mode.css doc/html/index.html 133 | cp $< $@ 134 | 135 | doc/html/haskell-mode.svg : images/haskell-mode.svg doc/html/index.html 136 | cp $< $@ 137 | 138 | doc/html/haskell-mode-32x32.png : images/haskell-mode-32x32.png doc/html/index.html 139 | cp $< $@ 140 | 141 | doc/html : doc/html/index.html \ 142 | doc/html/haskell-mode.css \ 143 | doc/html/haskell-mode.svg \ 144 | doc/html/haskell-mode-32x32.png 145 | 146 | deploy-manual : doc/html 147 | cd doc && ./deploy-manual.sh 148 | 149 | $(AUTOLOADS): $(ELFILES) 150 | $(BATCH) \ 151 | --eval '(setq make-backup-files nil)' \ 152 | --eval "(setq generated-autoload-file (concat command-line-default-directory \"/\" \"$@\"))" \ 153 | --eval "(require 'autoload)" \ 154 | -f batch-update-autoloads "." 155 | # check if autoloads will really load 156 | $(BATCH) -l "$@" 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Haskell Mode for Emacs 2 | ====================== 3 | 4 | This is an Emacs mode for editing, developing and debugging Haskell 5 | programs. [Home page](http://haskell.github.io/haskell-mode/). 6 | 7 | [![Build Status](https://github.com/haskell/haskell-mode/actions/workflows/test.yml/badge.svg)](https://github.com/haskell/haskell-mode/actions/workflows/test.yml) 8 | [![Melpa Status](http://melpa.org/packages/haskell-mode-badge.svg)](http://melpa.org/#/haskell-mode) 9 | [![Melpa Stable Status](http://stable.melpa.org/packages/haskell-mode-badge.svg)](http://stable.melpa.org/#/haskell-mode) 10 | [![Non-GNU ELPA Status](https://elpa.nongnu.org/nongnu/haskell-mode.svg)](https://elpa.nongnu.org/nongnu/haskell-mode.html) 11 | [![License GPL3](https://img.shields.io/badge/license-GPL3-blue.svg)](https://github.com/haskell/haskell-mode/blob/master/COPYING) 12 | 13 | > I just want to thank everybody involved in one way or another with the Haskell Emacs tooling. 14 | > It is one of the best language experiences I had in Emacs. 15 | > -- [cocreature, 2015-03-01](https://www.reddit.com/r/haskell/comments/2xjum3/haskellmode_february_2015_developments/cp0qa9a) 16 | 17 | > I've been using it for a long time and love it. Great work on haskell-mode! Keep up the good work! 18 | > -- [LukeHoersten, 2015-04-02](https://www.reddit.com/r/haskell/comments/316hcm/month_in_haskell_mode_march_2015/cpyutph) 19 | 20 | > This sounds wonderful. Does anything similar exist for Vim? 21 | > -- [earldouglas, 2015-07-02](https://www.reddit.com/r/haskell/comments/3bsa0f/month_in_haskell_mode_june_2015/cspdbb6) 22 | 23 | Users manual: [latest 24 | version](http://haskell.github.io/haskell-mode/manual/latest/), older 25 | versions: 26 | [13.12](http://haskell.github.io/haskell-mode/manual/13.12/), 27 | [13.14](http://haskell.github.io/haskell-mode/manual/13.14/), 28 | [13.16](http://haskell.github.io/haskell-mode/manual/13.16/), 29 | [13.18](http://haskell.github.io/haskell-mode/manual/13.18/), 30 | [13.20](http://haskell.github.io/haskell-mode/manual/13.20/). 31 | 32 | 33 | ## Quick Installation 34 | 35 | Make sure you have this in your [init 36 | file](http://www.gnu.org/software/emacs/manual/html_node/emacs/Init-File.html) 37 | (usually `~/.emacs`). If you already have `custom-set-variables`, 38 | merge its contents: 39 | 40 | ```elisp 41 | (require 'package) 42 | (custom-set-variables 43 | ;; custom-set-variables was added by Custom. 44 | ;; If you edit it by hand, you could mess it up, so be careful. 45 | ;; Your init file should contain only one such instance. 46 | ;; If there is more than one, they won't work right. 47 | '(package-archives 48 | (quote 49 | (("gnu" . "https://elpa.gnu.org/packages/") 50 | ("melpa" . "https://melpa.org/packages/"))))) 51 | (package-initialize) 52 | ``` 53 | 54 | Then run emacs, and evaluate: 55 | 56 | M-x package-refresh-contents 57 | 58 | and then follow by 59 | 60 | M-x package-install RET haskell-mode 61 | 62 | Voilà! `haskell-mode` is installed! You should be able to edit Haskell 63 | source code in color now. 64 | 65 | `Haskell-mode` has much much more to offer but the above should get 66 | you going! 67 | 68 | 69 | ## Advanced configuration 70 | 71 | For setup instructions, please consult the integrated `haskell-mode` 72 | Info manual which can be accessed after installation via `M-x 73 | info-display-manual [RET] haskell-mode`. Alternatively, you can also 74 | direct your browser to the [the online haskell-mode 75 | manual](http://haskell.github.io/haskell-mode/manual/latest/) for 76 | setup and user guide. 77 | 78 | 79 | ## Installation - more information 80 | 81 | `haskell-mode` supports GNU Emacs version 25.1 or later. 82 | 83 | `haskell-mode` is available from [MELPA 84 | Stable](http://stable.melpa.org) (releases) and 85 | [MELPA](http://melpa.org) (git snapshots). The latter will generally 86 | be considerably more up-to-date, and is recommended for most users. 87 | 88 | Other means of obtaining `haskell-mode` include 89 | [el-get](https://github.com/dimitri/el-get), [Emacs 90 | Prelude](https://github.com/bbatsov/prelude) and [Debian 91 | package](https://packages.debian.org/search?keywords=haskell-mode). 92 | 93 | ## Installation from git repository 94 | 95 | Running `haskell-mode` directly from sources is easy but 96 | requires a little preparation: 97 | 98 | - `git clone https://github.com/haskell/haskell-mode.git` into a 99 | suitable directory, e.g. `~/lib/emacs/haskell-mode/` where `~` 100 | stands for your home directory. 101 | 102 | - Assuming you have unpacked the various haskell-mode modules 103 | (`haskell-mode.el` and the rest) in the directory 104 | `~/lib/emacs/haskell-mode/`, you need to generate various files, the 105 | autoloads file (`haskell-mode-autoloads.el`) is one among 106 | them. Invoke: 107 | 108 | ```bash 109 | make EMACS=/path/to/your/emacs 110 | ``` 111 | 112 | and then adding the following command to your `.emacs`: 113 | 114 | ```el 115 | (add-to-list 'load-path "~/lib/emacs/haskell-mode/") 116 | (require 'haskell-mode-autoloads) 117 | (add-to-list 'Info-default-directory-list "~/lib/emacs/haskell-mode/") 118 | ``` 119 | 120 | ### Installation from git repository on macOS 121 | 122 | There are a couple of things to note if you want to install directly from 123 | git on macOS systems, as of version 10.13 High Sierra: 124 | 125 | - The version of makeinfo that is installed by 126 | default in /usr/bin is quite old and will cause the above make 127 | command to exit with an error. Installing the texinfo package using 128 | [Homebrew](https://brew.sh) will fix this. Be sure to follow the post-install instructions 129 | to add its bin directory to your shell's PATH variable. 130 | 131 | - If you are running an Emacs distribution packaged as a macOS application. such as 132 | the one available at https://emacsformacosx.com/, you'll need to add its executable 133 | to your PATH before the system's default Emacs version. That project's 134 | [Tips and Tricks](https://emacsformacosx.com/tips) page has detailed instructions. 135 | 136 | ## Contributing 137 | 138 | If you followed the above you are just a couple of steps away from 139 | contributing to `haskell-mode`. 140 | 141 | `haskell-mode` is actively seeking contributions from users of 142 | `haskell-mode`. For more information have a look at 143 | [the wiki page on contributing](https://github.com/haskell/haskell-mode/wiki/Contributing). 144 | 145 | 146 | -------------------------------------------------------------------------------- /doc/anim/company-mode-import-statement.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/company-mode-import-statement.gif -------------------------------------------------------------------------------- /doc/anim/company-mode-language-pragma.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/company-mode-language-pragma.gif -------------------------------------------------------------------------------- /doc/anim/flyspell-prog-mode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/flyspell-prog-mode.gif -------------------------------------------------------------------------------- /doc/anim/font-lock-quasi-quotes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/font-lock-quasi-quotes.gif -------------------------------------------------------------------------------- /doc/anim/font-lock-types.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/font-lock-types.gif -------------------------------------------------------------------------------- /doc/anim/font-lock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/font-lock.gif -------------------------------------------------------------------------------- /doc/anim/string-escape-highlight.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/doc/anim/string-escape-highlight.gif -------------------------------------------------------------------------------- /doc/deploy-manual.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -u 5 | 6 | if [[ "${TRAVIS_REPO_SLUG:-}" != "haskell/haskell-mode" ]]; then 7 | echo "TRAVIS_REPO_SLUG is '${TRAVIS_REPO_SLUG:-}' expected 'haskell/haskell-mode'" 8 | echo "Manual deployment available only directly for 'haskell/haskell-mode' repo" 9 | exit 0 10 | fi 11 | 12 | if [[ "${TRAVIS_BRANCH:-}" != "master" && "${TRAVIS_BRANCH:-}" != branch-* ]]; then 13 | echo "TRAVIS_BRANCH is '${TRAVIS_BRANCH:-}' expected 'master' or 'branch-*'" 14 | echo "Manual deployment available only for 'master' branch" 15 | exit 0 16 | fi 17 | 18 | if [[ -z "${GITHUB_DEPLOY_KEY_PASSPHRASE:-}" ]]; then 19 | echo "GITHUB_DEPLOY_KEY_PASSPHRASE must be set to passphrase for github deploy key" 20 | echo "Pull requests do not have access to secure variables" 21 | exit 0 22 | fi 23 | 24 | if [[ ${GITHUB_DEPLOY_KEY_PASSPHRASE:-} != "skip" ]]; then 25 | # Note: GITHUB_DEPLOY_KEY_PASSPHRASE comes from 'secure' section in .travis.yml 26 | cp haskell-mode-travis-deploy-key haskell-mode-travis-deploy-key-plain 27 | chmod 0600 haskell-mode-travis-deploy-key-plain 28 | ssh-keygen -f haskell-mode-travis-deploy-key-plain -P $GITHUB_DEPLOY_KEY_PASSPHRASE -p -N "" 29 | 30 | eval $(ssh-agent) 31 | ssh-add haskell-mode-travis-deploy-key-plain 32 | fi 33 | 34 | # Git setup, this commit should appear as if Travis made it 35 | export GIT_COMMITTER_EMAIL='travis@travis-ci.org' 36 | export GIT_COMMITTER_NAME='Travis CI' 37 | export GIT_AUTHOR_EMAIL='travis@travis-ci.org' 38 | export GIT_AUTHOR_NAME='Travis CI' 39 | 40 | # Documentation directory name 41 | 42 | if [[ ${TRAVIS_BRANCH} == "master" ]]; then 43 | DOCDIR="latest" 44 | else 45 | DOCDIR="${TRAVIS_BRANCH//branch-/}" 46 | fi 47 | 48 | HEAD_COMMIT=$(git rev-parse --short HEAD) 49 | 50 | if [ -d gh-pages-deploy ]; then 51 | rm -fr gh-pages-deploy 52 | fi 53 | 54 | git clone --quiet --depth 1 --branch=gh-pages "git@github.com:haskell/haskell-mode.git" gh-pages-deploy 55 | 56 | cd gh-pages-deploy 57 | if [[ -d "manual/${DOCDIR}" ]]; then 58 | git rm -qr "manual/${DOCDIR}" 59 | fi 60 | 61 | cp -r ../html "manual/${DOCDIR}" 62 | find "manual/${DOCDIR}" -name '*.html' -exec sed -i '~' -e '/^<\/head>$/i\ 63 | 64 | ' \{} \; 65 | find "manual/${DOCDIR}" -name '*~' -exec rm \{} \; 66 | git add "manual/${DOCDIR}" 67 | if [[ ${GITHUB_DEPLOY_KEY_PASSPHRASE:-} != "skip" ]]; then 68 | (git commit -m "Update manual for '${DOCDIR}' from haskell/haskell-mode@${HEAD_COMMIT}" && git push origin gh-pages) || true 69 | else 70 | echo "Update manual for '${DOCDIR}' from haskell/haskell-mode@${HEAD_COMMIT}" 71 | fi 72 | cd .. 73 | 74 | if [[ ${GITHUB_DEPLOY_KEY_PASSPHRASE:-} != "skip" ]]; then 75 | rm -fr gh-pages-deploy 76 | eval $(ssh-agent -k) 77 | fi 78 | 79 | echo Done! 80 | -------------------------------------------------------------------------------- /doc/gifcasts/get-window-id.m: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) 6 | { 7 | NSArray *windows = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements,kCGNullWindowID); 8 | for(NSDictionary *window in windows) { 9 | if ([[window objectForKey:(NSString *)kCGWindowOwnerPID] isEqual:[NSNumber numberWithLongLong:atoi(argv[1])]]) { 10 | if ([[window objectForKey:(NSString *)kCGWindowName] isEqual:[NSString stringWithUTF8String:argv[2]]]) { 11 | printf("%d\n", [[window objectForKey:(NSString *)kCGWindowNumber] intValue]); 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /doc/gifcasts/haskell-gifcasts.el: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; 3 | ;; This files is supposed to be run from command line like this: 4 | ;; 5 | ;; $EMACS -l haskell-gifcasts.el -f gifcast-generate-batch-and-exit 6 | ;; 7 | 8 | (require 'gifcast) 9 | (require 'company) 10 | (require 'shakespeare-mode) 11 | 12 | (setq load-path `("../.." ,@load-path)) 13 | (require 'haskell-mode-autoloads) 14 | (require 'haskell-mode) 15 | (require 'haskell-interactive-mode) 16 | 17 | (setq debug-on-error t) 18 | 19 | (gifcast-animation 20 | font-lock 21 | (progn 22 | (set-frame-size (window-frame (get-buffer-window)) 40 10) 23 | (when (get-buffer "Main.hs") 24 | (kill-buffer "Main.hs")) 25 | (switch-to-buffer (get-buffer-create "Main.hs")) 26 | (delete-other-windows) 27 | (tabbar-mode -1) 28 | (tool-bar-mode -1) 29 | (linum-mode -1) 30 | (message nil) 31 | (scroll-bar-mode -1) 32 | 33 | (haskell-mode) 34 | 35 | (insert (concat 36 | "-- | Program entry point\n" 37 | "main :: IO ()\n" 38 | "main = do\n" 39 | " if flag\n" 40 | " then putStrLn \"Flag is True\"\n" 41 | " else putStrLn \"Flag is False\"\n" 42 | ))) 43 | (global-font-lock-mode -1) 44 | (gifcast-capture) 45 | (global-font-lock-mode t) 46 | (gifcast-capture) 47 | (gifcast-generate "../anim/font-lock.gif") 48 | (kill-buffer "Main.hs")) 49 | 50 | (gifcast-animation 51 | font-lock-types 52 | (progn 53 | (set-frame-size (window-frame (get-buffer-window)) 60 10) 54 | (when (get-buffer "Main.hs") 55 | (kill-buffer "Main.hs")) 56 | (switch-to-buffer (get-buffer-create "Main.hs")) 57 | (delete-other-windows) 58 | (tabbar-mode -1) 59 | (tool-bar-mode -1) 60 | (linum-mode -1) 61 | (message nil) 62 | (scroll-bar-mode -1) 63 | 64 | (haskell-mode) 65 | 66 | (insert "data ExampleType a where\n" 67 | " ExampleConstructor :: Int -> ExampleType Int\n" 68 | "\n" 69 | "fun :: Maybe a -> IO Int\n" 70 | "fun (Just x) = do\n" 71 | " y :: Int <- (Prelude.++ 1) `fmap` readInt (_ :: Proxy Int)\n") 72 | (custom-set-faces 73 | '(haskell-type-face ((t :inherit font-lock-function-name-face))))) 74 | (global-font-lock-mode -1) 75 | (gifcast-capture) 76 | (global-font-lock-mode t) 77 | (gifcast-capture) 78 | (gifcast-generate "../anim/font-lock-types.gif") 79 | (kill-buffer "Main.hs")) 80 | 81 | 82 | (gifcast-animation 83 | font-lock-quasi-quotes 84 | (progn 85 | (set-frame-size (window-frame (get-buffer-window)) 50 10) 86 | (when (get-buffer "Main.hs") 87 | (kill-buffer "Main.hs")) 88 | (switch-to-buffer (get-buffer-create "Main.hs")) 89 | (delete-other-windows) 90 | (tabbar-mode -1) 91 | (tool-bar-mode -1) 92 | (linum-mode -1) 93 | (message nil) 94 | (scroll-bar-mode -1) 95 | 96 | (haskell-mode) 97 | 98 | (insert "html = [shamlet|\n" 99 | " $doctype 5\n" 100 | " \n" 101 | " \n" 102 | " #{pageTitle} - My Site\n" 103 | " <link rel=stylesheet href=@{Stylesheet}>\n" 104 | " |]\n") 105 | (custom-set-faces 106 | '(haskell-type-face ((t :inherit font-lock-function-name-face))))) 107 | (global-font-lock-mode -1) 108 | (gifcast-capture) 109 | (global-font-lock-mode t) 110 | (gifcast-capture) 111 | (gifcast-generate "../anim/font-lock-quasi-quotes.gif") 112 | (kill-buffer "Main.hs")) 113 | 114 | (gifcast-animation 115 | company-mode-language-pragma 116 | (set-frame-size (window-frame (get-buffer-window)) 40 10) 117 | (progn 118 | (when (get-buffer "Main.hs") 119 | (kill-buffer "Main.hs")) 120 | (switch-to-buffer (get-buffer-create "Main.hs")) 121 | (delete-other-windows) 122 | (tabbar-mode -1) 123 | (tool-bar-mode -1) 124 | (linum-mode -1) 125 | (blink-cursor-mode -1) 126 | (message nil) 127 | (scroll-bar-mode -1) 128 | 129 | (haskell-mode) 130 | (interactive-haskell-mode) 131 | (company-mode) 132 | (setq company-idle-delay 0.01) 133 | (linum-mode -1) 134 | 135 | (insert (concat 136 | "{-# LANGUAGE #-}\n" 137 | "main :: IO ()\n" 138 | "main = return ()\n")) 139 | (goto-char (+ 13 (point-min)))) 140 | (gifcast-capture) 141 | (gifcast-keys "F") 142 | (gifcast-capture) 143 | (gifcast-keys "l") 144 | (gifcast-capture) 145 | (gifcast-keys "e") 146 | (gifcast-capture) 147 | (gifcast-keys (kbd "<down>")) 148 | (gifcast-capture) 149 | (gifcast-keys "\C-m") 150 | (gifcast-capture) 151 | (gifcast-generate "../anim/company-mode-language-pragma.gif") 152 | 153 | (kill-buffer "Main.hs") 154 | nil) 155 | 156 | (gifcast-animation 157 | company-mode-import-statement 158 | (progn 159 | (set-frame-size (window-frame (get-buffer-window)) 70 15) 160 | (when (get-buffer "Main.hs") 161 | (kill-buffer "Main.hs")) 162 | (switch-to-buffer (set-buffer (get-buffer-create "Main.hs"))) 163 | (set-visited-file-name "Main.hs" t) 164 | (delete-other-windows) 165 | (tabbar-mode -1) 166 | (tool-bar-mode -1) 167 | (linum-mode -1) 168 | (blink-cursor-mode -1) 169 | (message nil) 170 | (scroll-bar-mode -1) 171 | 172 | (haskell-mode) 173 | (interactive-haskell-mode) 174 | (company-mode) 175 | (setq company-idle-delay 0.01) 176 | (linum-mode -1) 177 | 178 | (insert (concat 179 | "\n\n\n" 180 | "main :: IO ()\n" 181 | "main = return ()\n")) 182 | (save-buffer) 183 | 184 | (haskell-process-load-file) 185 | (sit-for 2) 186 | (select-window (get-buffer-window "Main.hs")) 187 | 188 | (goto-char (+ 1 (point-min)))) 189 | (gifcast-keys "") 190 | (gifcast-capture) 191 | (gifcast-keys "import") 192 | (gifcast-capture) 193 | (gifcast-keys " Control.") 194 | (gifcast-capture) 195 | (gifcast-keys "A") 196 | (gifcast-capture) 197 | (gifcast-keys "\C-m") 198 | (gifcast-capture) 199 | (gifcast-generate "../anim/company-mode-import-statement.gif") 200 | 201 | (haskell-kill-session-process) 202 | (set-buffer-modified-p nil) 203 | (kill-buffer "Main.hs")) 204 | 205 | (gifcast-animation 206 | string-escape-highlight 207 | (progn 208 | (set-frame-size (window-frame (get-buffer-window)) 40 5) 209 | (when (get-buffer "Main.hs") 210 | (kill-buffer "Main.hs")) 211 | (switch-to-buffer (set-buffer (get-buffer-create "Main.hs"))) 212 | (delete-other-windows) 213 | (tabbar-mode -1) 214 | (tool-bar-mode -1) 215 | (linum-mode -1) 216 | (blink-cursor-mode -1) 217 | (message nil) 218 | (scroll-bar-mode -1) 219 | 220 | (haskell-mode) 221 | (linum-mode -1)) 222 | 223 | (gifcast-keys "\nmsg = ") 224 | (gifcast-capture 10) 225 | (gifcast-keys "\"") 226 | (gifcast-capture 10) 227 | (gifcast-keys "Hello") 228 | (gifcast-capture 10) 229 | (gifcast-keys "World") 230 | (gifcast-capture 10) 231 | (gifcast-keys "\"") 232 | (gifcast-capture 10) 233 | (gifcast-keys (kbd "<left><left><left><left><left><left>")) 234 | (gifcast-capture 10) 235 | (gifcast-keys "\\") 236 | (gifcast-capture 10) 237 | (gifcast-keys "x") 238 | (gifcast-capture 10) 239 | (gifcast-keys "20") 240 | (gifcast-capture 500) 241 | (gifcast-generate "../anim/string-escape-highlight.gif") 242 | 243 | (kill-buffer "Main.hs") 244 | nil) 245 | 246 | (gifcast-animation 247 | flyspell-prog-mode 248 | (progn 249 | (set-frame-size (window-frame (get-buffer-window)) 70 10) 250 | (when (get-buffer "Main.hs") 251 | (kill-buffer "Main.hs")) 252 | (switch-to-buffer (get-buffer-create "Main.hs")) 253 | (delete-other-windows) 254 | (tabbar-mode -1) 255 | (tool-bar-mode -1) 256 | (linum-mode -1) 257 | (message nil) 258 | (scroll-bar-mode -1) 259 | 260 | (haskell-mode) 261 | 262 | (insert "\n\n\n\n" 263 | "main = do\n" 264 | " putStrLn \"Heskell is a realy nice lanuage\"\n" 265 | "\n\n\n\n\n\n\n\n\n") 266 | (goto-char (point-min))) 267 | 268 | (gifcast-capture) 269 | 270 | (gifcast-keys (kbd "M-x")) 271 | (gifcast-capture) 272 | (gifcast-keys "flyspell-prog-mode") 273 | (gifcast-capture) 274 | (gifcast-keys (kbd "RET")) 275 | 276 | (progn 277 | (flyspell-prog-mode) 278 | (flyspell-region (point-min) (point-max))) 279 | 280 | (gifcast-capture) 281 | 282 | (re-search-forward "Heskell") 283 | (gifcast-capture) 284 | (gifcast-keys (kbd "M-$")) 285 | (gifcast-capture) 286 | (gifcast-keys "0") 287 | (gifcast-capture) 288 | 289 | 290 | (re-search-forward "real") 291 | (gifcast-capture) 292 | (gifcast-keys (kbd "M-$")) 293 | (gifcast-capture) 294 | (gifcast-keys "0") 295 | (gifcast-capture) 296 | 297 | (re-search-forward "lan") 298 | (gifcast-capture) 299 | (gifcast-keys (kbd "M-$")) 300 | (gifcast-capture) 301 | (gifcast-keys "0") 302 | (gifcast-capture) 303 | 304 | (gifcast-generate "../anim/flyspell-prog-mode.gif") 305 | (kill-buffer "Main.hs")) 306 | -------------------------------------------------------------------------------- /doc/haskell-manual-fixups.el: -------------------------------------------------------------------------------- 1 | 2 | 3 | (defun get-gif-dimensions (filename) 4 | "Get GIF dimensions, return a cons of (w,h). 5 | 6 | Get GIF dimensions directly from binary. Does not need external 7 | tools. 8 | 9 | 10 | GIF Header 11 | 12 | Offset Length Contents 13 | 0 3 bytes \"GIF\" 14 | 3 3 bytes \"87a\" or \"89a\" 15 | 6 2 bytes <Logical Screen Width> 16 | 8 2 bytes <Logical Screen Height> 17 | 10 1 byte bit 0: Global Color Table Flag (GCTF) 18 | bit 1..3: Color Resolution 19 | bit 4: Sort Flag to Global Color Table 20 | bit 5..7: Size of Global Color Table: 2^(1+n) 21 | 11 1 byte <Background Color Index> 22 | 12 1 byte <Pixel Aspect Ratio> 23 | 13 ? bytes <Global Color Table(0..255 x 3 bytes) if GCTF is one> 24 | ? bytes <Blocks> 25 | 1 bytes <Trailer> (0x3b)" 26 | (interactive "fFile name:") 27 | (with-current-buffer (get-buffer-create "*GIF*") 28 | (set-buffer-multibyte nil) 29 | (insert-file-contents-literally filename nil 0 10 t) 30 | (when (not (looking-at-p "GIF8[79]a")) 31 | (error "File '%s' is not a GIF" filename)) 32 | (let ((result 33 | (cons (+ (char-after 7) (* 256 (char-after 8))) 34 | (+ (char-after 9) (* 256 (char-after 10)))))) 35 | (if (called-interactively-p) 36 | (message "Dimensions: %dx%d" (car result) (cdr result))) 37 | result))) 38 | 39 | (defun haskell-manual-fixup-buffer (&optional buffer) 40 | "Fix contents of HTML from makeinfo in a BUFFER. 41 | 42 | Currently it looks for image references and adds an explicit 43 | width and height. GIFs are generate on Retina so their resolution 44 | is double of what it should be. Here we halve it to compensate 45 | dimensions and to keep it crisp when viewed on Retina again." 46 | (interactive) 47 | (with-current-buffer (or buffer (current-buffer)) 48 | (save-excursion 49 | (goto-char (point-min)) 50 | (while (re-search-forward "<img src=\"\\(.*\\)\" alt=\"\\(.*\\)\">" nil t) 51 | (let* ((filename (match-string-no-properties 1)) 52 | (alttext (match-string-no-properties 2)) 53 | (default-directory (file-name-directory (buffer-file-name))) 54 | (dim (get-gif-dimensions filename)) 55 | (img (format "<img width=\"%d\" height=\"%d\" src=\"%s\" alt=\"%s\">" 56 | (/ (car dim) 2) (/ (cdr dim) 2) filename alttext))) 57 | (delete-region (match-beginning 0) (match-end 0)) 58 | (insert img)))))) 59 | 60 | (defun haskell-manual-fixup-file (filename) 61 | "Run `haskell-manual-fixup-buffer' on a file." 62 | (interactive "fFile name:") 63 | (with-temp-buffer 64 | (insert-file-contents filename t) 65 | (haskell-manual-fixup-buffer) 66 | (when (buffer-modified-p) 67 | (basic-save-buffer)))) 68 | 69 | (defun haskell-manual-fixups-batch-and-exit () 70 | "Run `haskell-manual-fixup-buffer' on files given as arguments. 71 | 72 | Should be invoked as: 73 | 74 | emacs -l haskell-manual-fixups.el -f haskell-manual-fixups-batch-and-exit doc/html/*.html" 75 | (dolist (filename command-line-args-left) 76 | (haskell-manual-fixup-file filename)) 77 | (kill-emacs 0)) 78 | -------------------------------------------------------------------------------- /doc/haskell-mode-travis-deploy-key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-128-CBC,9385B3F19E12C488ACF654D37A7B70B2 4 | 5 | 3ucmv5i37lwRejeFWTliQ9pfs8Vfq1G/30lnI7/GroClDiE2LAKm9tWzD8EAVdjx 6 | lAcD9gKy9g+5Gft/qj+ucXmh2OWhTz2veBIJzjuujoZBNbbgBhBGrkAzr0h2LK5u 7 | OE06F5Goz6rAjnyFbxTU9slZnWENHKpnwYBRriIq3KM1vS9Kr/0S3cYYfY5exITT 8 | Gy5uTLHDVzps5Bn7wjLgXRUoLbDRRDF6L4LPkaFyEHRt+1VjtqWc3jnMuY7P2Rru 9 | y8Mlr0cv9UzeJleTrN8AvcEtUCcl7FpieSZsGh0Q2/wEj3fEFk6Zf/q3aAqz0mn4 10 | hdp3CvTRUeYv1JibdXSptd34dpxB+9IJIbZnZ4ixaj5Ay3oCj9PPHC+qDGsjeA6J 11 | ect9EzxuVr6j4gvJYR0nOKNH1KOBOAZDhCQG0kvZxXaQyUbu5NV+kh6XUCedzfJZ 12 | XyutuOF6frUjEkJlRj5aWdgtXUDKrPDO9jnV9jknoyaOQMjWt4vd8uhkWZo+SfZ8 13 | bVO8pcx/YxNBKiHeDiLVUPc7h0Y1fI48RwLky+iLrYTe6hhMOPb8/PxZsbG+xVN5 14 | +D2v08EhDzomxsPKBagHCb9Dffihi21F54ZeRBAx4Fr7O3KEHFE/67Oec5lz/seh 15 | yDw+ZhW9bLklO8nZWw4rJGKG7pCgb4pRVD4Rk3VFgVdolq9Z44YNCyHX+CsfuE7D 16 | KvHBOQWdpVD8EvP2sjqb7kIFOY9ZP5vOOZ10fm9XweCca4xJzwlr9aah0XXYza8S 17 | hCgkPYDQndLM4NH0UfGJPft7HhjUSF7vodL/0jKsTvRBeXnh5pgeTM576LnpfYd3 18 | wzLKg4g2/bIMUGYKo4VRY6rXvQbf4MDvUlnGRMBnihbm+x0LU6U0/nxBzeKnv5D9 19 | VR56xGwJHxIytNjqzAZVPk8+9nXw8Dyc0XmSkPsaYr5DgI/nacRbdvlVo2M8Rac8 20 | 72qcAqkUAB/JXl4Jbucw4nGBsdOnl6zGRqTMeG/bfR9ULM4u1wGzNeXghc5+Krk3 21 | mMiZeCxfnAdw2eEM0aXieMD2QB882Cm5HVB+7tqQZcbpr8pt9uNQrr44rkBnNsqa 22 | LQdmbUN+1+uDXlFstkKBCN/i2cEX5NTo7yiebMZZ9/+uY1LWTPzV3wAwtJeYaMIB 23 | oJ5DunlxqpKULX8jL0k2qAuA57ah2DlsMj/2L9uNs84ZmkPnhtPpiDwsFCL+xxRo 24 | Sup6qrrBOP6WvGR8O4uWcKP/HH6x11cgD20NWcoSjslXBgJK5nV9uqiKE/vNjG9e 25 | /pDfvce4kdUQcp4hbJu+o8MrWLC9o3ijPZWfqikCt5dX8qMp364GZcyxYOy0gv9X 26 | g259f1GImJHcFMb88qqYdP9au+S96ZIXOGtlKH/nwfe/UFe9tO2jOQqNgVh+Akyu 27 | 3gcjuMyDQryXNNHMRlB76mFAUnSUQIQ3n84oMFn8ZscogDdA89FWOV0MUkSoiVmW 28 | 7uD2hlliYg269xDJH3FE4Txk95fKquJpsa+6hzpt2V7VqR6m0BzQ+yQmMFuWsLtI 29 | oTOUtdPYojZVWPgeskNCrxEg2/CJQ4lHGQhv8tab5HWleHCS75RjwlFpYE9Ie4K3 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /doc/haskell-mode.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | @import url("http://fonts.googleapis.com/css?family=Lato:400,400italic,700,700italic&subset=latin,latin-ext"); 3 | @import url("http://fonts.googleapis.com/css?family=Source Code Pro"); 4 | 5 | body { 6 | font-family: Lato, Arial, sans-serif; 7 | font-feature-settings: "kern", "liga", "clig", "calt"; 8 | font-size: 1em; 9 | line-height: 1.3em; 10 | } 11 | body { 12 | width: 800px; 13 | margin: 0 auto; 14 | } 15 | table.menu { 16 | width: 700px; 17 | margin-left: 50px; 18 | } 19 | /* makeinfo 5.2 */ 20 | div.header p { 21 | margin: 0; 22 | text-align: right; 23 | } 24 | div.header { 25 | background-color: #9a98bd; 26 | background: linear-gradient(#CDCCDE,#A9A7CD); 27 | } 28 | /* makeinfo 4.8 */ 29 | div.node { 30 | background-color: #9a98bd; 31 | background: linear-gradient(#CDCCDE,#A9A7CD); 32 | } 33 | div.node p { 34 | margin: 0; 35 | text-align: right; 36 | } 37 | div.node hr { 38 | margin: 0; 39 | } 40 | 41 | div.footnote h3 { 42 | display: inline; 43 | font-size: normal; 44 | } 45 | /* makeinfo up to 5.2 cannot pass on unicode characters without messing them up */ 46 | a[rel="up"]:before { 47 | content: " \2191 "; /* ↑ */ 48 | } 49 | a[rel="prev"]:before { 50 | content: " \2190 "; /* ← */ 51 | } 52 | a[rel="next"]:before { 53 | content: " \2192 "; /* → */ 54 | } 55 | code, kbd, samp, pre { 56 | font-size: 85%; 57 | font-family: "Source Code Pro", Menlo, Inconsolata, monospace; 58 | } 59 | code, pre, samp { 60 | background-color: rgba(0,0,0,0.04); 61 | border-radius: 3px; 62 | } 63 | 64 | kbd { 65 | display: inline-block; 66 | padding: 3px 5px; 67 | font-style:normal; 68 | line-height: 0.8em; 69 | color: #555; 70 | vertical-align: middle; 71 | background-color: #fcfcfc; 72 | border: solid 1px #ccc; 73 | border-bottom-color: #bbb; 74 | border-radius: 3px; 75 | box-shadow: inset 0 -1px 0 #bbb; 76 | } 77 | 78 | div.background { 79 | position: fixed; 80 | z-index: -1; 81 | right: 30px; 82 | bottom: 0px; 83 | width: 256px; 84 | height: 256px; 85 | opacity: 0.3; 86 | background-image: url("haskell-mode.svg"); 87 | background-repeat: no-repeat; 88 | background-size: 256px 256px; 89 | } 90 | 91 | img { 92 | /* same as div.smallexample in default rules */ 93 | margin-left: 3.2em; 94 | } 95 | -------------------------------------------------------------------------------- /ghc-core.el: -------------------------------------------------------------------------------- 1 | ;;; ghc-core.el --- Syntax highlighting module for GHC Core -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2010 Johan Tibell 4 | 5 | ;; Author: Johan Tibell <johan.tibell@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; Purpose: 27 | ;; 28 | ;; To make it easier to read GHC Core output by providing highlighting 29 | ;; and removal of commonly ignored annotations. 30 | 31 | ;;; Code: 32 | (require 'haskell-mode) 33 | (require 'haskell-font-lock) 34 | 35 | (defgroup ghc-core nil 36 | "Major mode for viewing pretty printed GHC Core output." 37 | :link '(custom-manual "(haskell-mode)") 38 | :group 'haskell 39 | :prefix "ghc-core-") 40 | 41 | (defcustom ghc-core-program 42 | "ghc" 43 | "Name of the GHC executable (excluding any arguments)." 44 | :type 'string 45 | :group 'ghc-core) 46 | 47 | (define-obsolete-variable-alias 'ghc-core-create-options 'ghc-core-program-args 48 | "haskell-mode 13.7") 49 | 50 | (defcustom ghc-core-program-args 51 | '("-O2") 52 | "Additional options to be passed to GHC when generating core output. 53 | GHC (see variable `ghc-core-program') is invoked with the basic 54 | command line options \"-ddump-simpl -c <source-file>\" 55 | followed by the additional options defined here. 56 | 57 | The following `-ddump-simpl` options might be of interest: 58 | 59 | - `-dsuppress-all' 60 | - `-dsuppress-uniques' 61 | - `-dsuppress-idinfo' 62 | - `-dsuppress-module-prefixes' 63 | - `-dsuppress-type-signatures' 64 | - `-dsuppress-type-applications' 65 | - `-dsuppress-coercions' 66 | 67 | See `M-x manual-entry RET ghc' for more details." 68 | :type '(repeat (string :tag "Argument")) 69 | :group 'ghc-core) 70 | 71 | (defun ghc-core-clean-region (start end) 72 | "Remove commonly ignored annotations and namespace prefixes 73 | in the region between START and END." 74 | (interactive "r") 75 | (save-restriction 76 | (narrow-to-region start end) 77 | (goto-char (point-min)) 78 | (while (search-forward-regexp "GHC\.[^\.]*\." nil t) 79 | (replace-match "" nil t)) 80 | (goto-char (point-min)) 81 | (while (flush-lines "^ *GblId *$" nil)) 82 | (goto-char (point-min)) 83 | (while (flush-lines "^ *LclId *$" nil)) 84 | (goto-char (point-min)) 85 | (while (flush-lines (concat "^ *\\[\\(?:Arity [0-9]+\\|NoCafRefs\\|" 86 | "Str: DmdType\\|Worker \\)" 87 | "\\([^]]*\\n?\\).*\\] *$") nil)) 88 | (goto-char (point-min)) 89 | (while (search-forward "Main." nil t) (replace-match "" nil t)))) 90 | 91 | (defun ghc-core-clean-buffer () 92 | "Remove commonly ignored annotations and namespace prefixes 93 | in the current buffer." 94 | (interactive) 95 | (ghc-core-clean-region (point-min) (point-max))) 96 | 97 | ;;;###autoload 98 | (defun ghc-core-create-core () 99 | "Compile and load the current buffer as tidy core." 100 | (interactive) 101 | (save-buffer) 102 | (let* ((core-buffer (generate-new-buffer "ghc-core")) 103 | (neh (lambda () (kill-buffer core-buffer)))) 104 | (add-hook 'next-error-hook neh) 105 | (apply #'call-process ghc-core-program nil core-buffer nil 106 | "-ddump-simpl" "-c" (buffer-file-name) ghc-core-program-args) 107 | (display-buffer core-buffer) 108 | (with-current-buffer core-buffer 109 | (ghc-core-mode)) 110 | (remove-hook 'next-error-hook neh))) 111 | 112 | ;;;###autoload 113 | (add-to-list 'auto-mode-alist '("\\.hcr\\'" . ghc-core-mode)) 114 | ;;;###autoload 115 | (add-to-list 'auto-mode-alist '("\\.dump-simpl\\'" . ghc-core-mode)) 116 | 117 | ;;;###autoload 118 | (define-derived-mode ghc-core-mode haskell-mode "GHC-Core" 119 | "Major mode for GHC Core files.") 120 | 121 | (provide 'ghc-core) 122 | ;;; ghc-core.el ends here 123 | -------------------------------------------------------------------------------- /ghci-script-mode.el: -------------------------------------------------------------------------------- 1 | ;;; ghci-script-mode.el --- GHCi scripts major mode -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'haskell) 21 | 22 | (defvar ghci-script-mode-keywords 23 | ;; The comment syntax can't be described simply in syntax-table. 24 | ;; We could use font-lock-syntactic-keywords, but is it worth it? 25 | '(("^[ \t]*--.*" . font-lock-comment-face) 26 | ("^ *\\([^ \t:]+\\):" (1 font-lock-keyword-face)) 27 | ("^:[a-z{]+ *\\+" . font-lock-keyword-face) 28 | ("^:[a-z{]+ " . font-lock-keyword-face))) 29 | 30 | ;;;###autoload 31 | (define-derived-mode ghci-script-mode text-mode "GHCi-Script" 32 | "Major mode for working with .ghci files." 33 | (setq-local adaptive-fill-mode nil) 34 | (setq-local comment-start "-- ") 35 | (setq-local comment-padding 0) 36 | (setq-local comment-start-skip "[-{]-[ \t]*") 37 | (setq-local comment-end "") 38 | (setq-local comment-end-skip "[ \t]*\\(-}\\|\\s>\\)") 39 | (setq-local font-lock-defaults '(ghci-script-mode-keywords t t nil nil)) 40 | (setq-local indent-tabs-mode nil) 41 | (setq-local tab-width 8) 42 | (when (boundp 'electric-indent-inhibit) 43 | (setq electric-indent-inhibit t)) 44 | (setq-local dabbrev-case-fold-search nil) 45 | (setq-local dabbrev-case-distinction nil) 46 | (setq-local dabbrev-case-replace nil) 47 | (setq-local dabbrev-abbrev-char-regexp "\\sw\\|[.]") 48 | (setq haskell-literate nil)) 49 | 50 | ;;;###autoload 51 | (add-to-list 'auto-mode-alist '("\\.ghci\\'" . ghci-script-mode)) 52 | 53 | (define-key ghci-script-mode-map (kbd "C-c C-l") 'ghci-script-mode-load) 54 | 55 | (defun ghci-script-mode-load () 56 | "Load the current script file into the GHCi session." 57 | (interactive) 58 | (save-buffer) 59 | (let ((filename (buffer-file-name)) 60 | (buffer (haskell-session-interactive-buffer (haskell-session)))) 61 | (with-current-buffer buffer 62 | (set-marker haskell-interactive-mode-prompt-start (point-max)) 63 | (haskell-interactive-mode-run-expr 64 | (concat ":script " filename))))) 65 | 66 | (provide 'ghci-script-mode) 67 | -------------------------------------------------------------------------------- /haskell-align-imports.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-align-imports.el --- Align the import lines in a Haskell file -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2010 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This program is free software: you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License as 11 | ;; published by the Free Software Foundation, either version 3 of 12 | ;; the License, or (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be 15 | ;; useful, but WITHOUT ANY WARRANTY; without even the implied 16 | ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 17 | ;; PURPOSE. See the GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public 20 | ;; License along with this program. If not, see 21 | ;; <http://www.gnu.org/licenses/>. 22 | 23 | ;;; Commentary: 24 | 25 | ;; Consider the following imports list: 26 | ;; 27 | ;; import One 28 | ;; import Two as A 29 | ;; import qualified Three 30 | ;; import qualified Four as PRELUDE 31 | ;; import Five (A) 32 | ;; import Six (A,B) 33 | ;; import qualified Seven (A,B) 34 | ;; import "abc" Eight 35 | ;; import "abc" Nine as TWO 36 | ;; import qualified "abc" Ten 37 | ;; import qualified "defg" Eleven as PRELUDE 38 | ;; import "barmu" Twelve (A) 39 | ;; import "zotconpop" Thirteen (A,B) 40 | ;; import qualified "z" Fourteen (A,B) 41 | ;; import Fifteen hiding (A) 42 | ;; import Sixteen as TWO hiding (A) 43 | ;; import qualified Seventeen hiding (A) 44 | ;; import qualified Eighteen as PRELUDE hiding (A) 45 | ;; import "abc" Nineteen hiding (A) 46 | ;; import "abc" Twenty as TWO hiding (A) 47 | ;; 48 | ;; When haskell-align-imports is run within the same buffer, the 49 | ;; import list is transformed to: 50 | ;; 51 | ;; import "abc" Eight 52 | ;; import qualified Eighteen as PRELUDE hiding (A) 53 | ;; import qualified "defg" Eleven as PRELUDE 54 | ;; import Fifteen hiding (A) 55 | ;; import Five (A) 56 | ;; import qualified Four as PRELUDE 57 | ;; import qualified "z" Fourteen (A,B) 58 | ;; import "abc" Nine as TWO 59 | ;; import "abc" Nineteen hiding (A) 60 | ;; import One 61 | ;; import qualified Seven (A,B) 62 | ;; import qualified Seventeen hiding (A) 63 | ;; import Six (A,B) 64 | ;; import Sixteen as TWO hiding (A) 65 | ;; import qualified "abc" Ten 66 | ;; import "zotconpop" Thirteen (A,B) 67 | ;; import qualified Three 68 | ;; import "barmu" Twelve (A) 69 | ;; import "abc" Twenty as TWO hiding (A) 70 | ;; import Two as A 71 | ;; 72 | ;; If you want everything after module names to be padded out, too, 73 | ;; customize `haskell-align-imports-pad-after-name', and you'll get: 74 | ;; 75 | ;; import One 76 | ;; import Two as A 77 | ;; import qualified Three 78 | ;; import qualified Four as PRELUDE 79 | ;; import Five (A) 80 | ;; import Six (A,B) 81 | ;; import qualified Seven (A,B) 82 | ;; import "abc" Eight 83 | ;; import "abc" Nine as TWO 84 | ;; import qualified "abc" Ten 85 | ;; import qualified "defg" Eleven as PRELUDE 86 | ;; import "barmu" Twelve (A) 87 | ;; import "zotconpop" Thirteen (A,B) 88 | ;; import qualified "z" Fourteen (A,B) 89 | ;; import Fifteen hiding (A) 90 | ;; import Sixteen as TWO hiding (A) 91 | ;; import qualified Seventeen hiding (A) 92 | ;; import qualified Eighteen as PRELUDE hiding (A) 93 | ;; import "abc" Nineteen hiding (A) 94 | ;; import "abc" Twenty as TWO hiding (A) 95 | 96 | ;;; Code: 97 | 98 | (require 'cl-lib) 99 | 100 | (defvar haskell-align-imports-regexp 101 | (concat "^\\(import[ ]+\\)" 102 | "\\(qualified \\)?" 103 | "[ ]*\\(\"[^\"]*\" \\)?" 104 | "[ ]*\\([A-Za-z0-9_.']+\\)" 105 | "[ ]*\\([ ]*as [A-Z][^ ]*\\)?" 106 | "[ ]*\\((.*)\\)?" 107 | "\\([ ]*hiding (.*)\\)?" 108 | "\\( -- .*\\)?[ ]*$") 109 | "Regex used for matching components of an import.") 110 | 111 | (defcustom haskell-align-imports-pad-after-name 112 | nil 113 | "Pad layout after the module name also." 114 | :type 'boolean 115 | :group 'haskell-interactive) 116 | 117 | ;;;###autoload 118 | (defun haskell-align-imports () 119 | "Align all the imports in the buffer." 120 | (interactive) 121 | (when (haskell-align-imports-line-match) 122 | (save-excursion 123 | (goto-char (point-min)) 124 | (let* ((imports (haskell-align-imports-collect)) 125 | (padding (haskell-align-imports-padding imports))) 126 | (mapc (lambda (x) 127 | (goto-char (cdr x)) 128 | (delete-region (point) (line-end-position)) 129 | (insert (haskell-align-imports-chomp 130 | (haskell-align-imports-fill padding (car x))))) 131 | imports)))) 132 | nil) 133 | 134 | (defun haskell-align-imports-line-match () 135 | "Try to match the current line as a regexp." 136 | (let ((line (buffer-substring-no-properties (line-beginning-position) 137 | (line-end-position)))) 138 | (if (string-match "^import " line) 139 | line 140 | nil))) 141 | 142 | (defun haskell-align-imports-collect () 143 | "Collect a list of mark / import statement pairs." 144 | (let ((imports '())) 145 | (while (not (or (equal (point) (point-max)) (haskell-align-imports-after-imports-p))) 146 | (let ((line (haskell-align-imports-line-match-it))) 147 | (when line 148 | (let ((match 149 | (haskell-align-imports-merge-parts 150 | (cl-loop for i from 1 to 8 151 | collect (haskell-align-imports-chomp (match-string i line)))))) 152 | (setq imports (cons (cons match (line-beginning-position)) 153 | imports))))) 154 | (forward-line)) 155 | imports)) 156 | 157 | (defun haskell-align-imports-merge-parts (l) 158 | "Merge together parts of an import statement that shouldn't be separated." 159 | (let ((parts (apply #'vector l)) 160 | (join (lambda (ls) 161 | (cl-reduce (lambda (a b) 162 | (concat a 163 | (if (and (> (length a) 0) 164 | (> (length b) 0)) 165 | " " 166 | "") 167 | b)) 168 | ls)))) 169 | (if haskell-align-imports-pad-after-name 170 | (list (funcall join (list (aref parts 0) 171 | (aref parts 1) 172 | (aref parts 2))) 173 | (aref parts 3) 174 | (funcall join (list (aref parts 4) 175 | (aref parts 5) 176 | (aref parts 6))) 177 | (aref parts 7)) 178 | (list (funcall join (list (aref parts 0) 179 | (aref parts 1) 180 | (aref parts 2))) 181 | (funcall join (list (aref parts 3) 182 | (aref parts 4) 183 | (aref parts 5) 184 | (aref parts 6) 185 | (aref parts 7))))))) 186 | 187 | (defun haskell-align-imports-chomp (str) 188 | "Chomp leading and tailing whitespace from STR." 189 | (if str 190 | (replace-regexp-in-string "\\(^[[:space:]\n]*\\|[[:space:]\n]*$\\)" "" 191 | str) 192 | "")) 193 | 194 | (defun haskell-align-imports-padding (imports) 195 | "Find the padding for each part of the import statements." 196 | (if (null imports) 197 | imports 198 | (cl-reduce (lambda (a b) (cl-mapcar #'max a b)) 199 | (mapcar (lambda (x) (mapcar #'length (car x))) 200 | imports)))) 201 | 202 | (defun haskell-align-imports-fill (padding line) 203 | "Fill an import line using the padding worked out from all statements." 204 | (mapconcat #'identity 205 | (cl-mapcar (lambda (pad part) 206 | (if (> (length part) 0) 207 | (concat part (make-string (- pad (length part)) ? )) 208 | (make-string pad ? ))) 209 | padding 210 | line) 211 | " ")) 212 | 213 | (defun haskell-align-imports-line-match-it () 214 | "Try to match the current line as a regexp." 215 | (let ((line (buffer-substring-no-properties (line-beginning-position) 216 | (line-end-position)))) 217 | (if (string-match haskell-align-imports-regexp line) 218 | line 219 | nil))) 220 | 221 | (defun haskell-align-imports-after-imports-p () 222 | "Are we after the imports list?" 223 | (save-excursion 224 | (goto-char (line-beginning-position)) 225 | (let ((case-fold-search nil)) 226 | (not (not (search-forward-regexp "\\( = \\|\\<instance\\>\\| :: \\| ∷ \\)" 227 | (line-end-position) t 1)))))) 228 | 229 | (provide 'haskell-align-imports) 230 | 231 | ;;; haskell-align-imports.el ends here 232 | -------------------------------------------------------------------------------- /haskell-c2hs.el: -------------------------------------------------------------------------------- 1 | ;; haskell-c2hs.el --- -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016 Sergey Vinokurov 4 | ;; 5 | ;; Author: Sergey Vinokurov <serg.foo@gmail.com> 6 | ;; Created: Monday, 7 March 2016 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 20 | 21 | ;;; Commentary: 22 | ;; This mode is mostly intended for highlighting {#...#} hooks. 23 | ;; 24 | ;; Quick setup: 25 | ;; (autoload 'haskell-c2hs-mode "haskell-c2hs-mode" nil t) 26 | ;; (add-to-list 'auto-mode-alist '("\\.chs\\'" . haskell-c2hs-mode)) 27 | ;; 28 | 29 | (require 'haskell-mode) 30 | (require 'haskell-font-lock) 31 | (require 'haskell-utils) 32 | 33 | ;;;###autoload 34 | (add-to-list 'auto-mode-alist '("\\.chs\\'" . haskell-c2hs-mode)) 35 | 36 | (defface haskell-c2hs-hook-pair-face 37 | '((t (:inherit font-lock-preprocessor-face))) 38 | "Face for highlighting {#...#} pairs." 39 | :group 'haskell) 40 | 41 | (defface haskell-c2hs-hook-name-face 42 | '((t (:inherit font-lock-keyword-face))) 43 | "Face for highlighting c2hs hook names." 44 | :group 'haskell) 45 | 46 | (defvar haskell-c2hs-font-lock-keywords 47 | `((,(eval-when-compile 48 | (let* ((ws '(any ?\s ?\t ?\n ?\r)) 49 | (anychar '(or (not (any ?#)) 50 | (seq "#" 51 | (not (any ?\}))))) 52 | (any-nonquote '(or (not (any ?# ?\")) 53 | (seq "#" 54 | (not (any ?\} ?\"))))) 55 | (cid '(seq (any (?a . ?z) (?A . ?Z) ?_) 56 | (* (any (?a . ?z) (?A . ?Z) (?0 . ?9) ?_)))) 57 | (hsid-type '(seq (? "'") 58 | (any (?A . ?Z)) 59 | (* (any (?a . ?z) (?A . ?Z) (?0 . ?9) ?_ ?')))) 60 | (equals-str-val `(seq (* ,ws) 61 | "=" 62 | (* ,ws) 63 | "\"" 64 | (* ,any-nonquote) 65 | "\""))) 66 | (eval 67 | `(rx 68 | (seq 69 | (group-n 1 "{#") 70 | (* ,ws) 71 | (or (seq (group-n 2 72 | "import" 73 | (opt (+ ,ws) 74 | "qualified")) 75 | (+ ,ws)) 76 | (seq (group-n 2 77 | "context") 78 | (opt (+ ,ws) 79 | (group-n 3 80 | "lib") 81 | ,equals-str-val) 82 | (opt (+ ,ws) 83 | (group-n 4 84 | "prefix") 85 | ,equals-str-val) 86 | (opt (+ ,ws) 87 | (group-n 5 88 | "add" 89 | (+ ,ws) 90 | "prefix") 91 | ,equals-str-val)) 92 | (seq (group-n 2 93 | "type") 94 | (+ ,ws) 95 | ,cid) 96 | (seq (group-n 2 97 | "sizeof") 98 | (+ ,ws) 99 | ,cid) 100 | (seq (group-n 2 101 | "enum" 102 | (+ ,ws) 103 | "define") 104 | (+ ,ws) 105 | ,cid) 106 | ;; TODO: vanilla enum fontification is incomplete 107 | (seq (group-n 2 108 | "enum") 109 | (+ ,ws) 110 | ,cid 111 | (opt (+ ,ws) 112 | (group-n 3 113 | "as"))) 114 | ;; TODO: fun hook highlighting is incomplete 115 | (seq (group-n 2 116 | (or "call" 117 | "fun") 118 | (opt (+ ,ws) 119 | "pure") 120 | (opt (+ ,ws) 121 | "unsafe")) 122 | (+ ,ws) 123 | ,cid 124 | (opt (+ ,ws) 125 | (group-n 3 126 | "as") 127 | (opt (+ ,ws) 128 | (group-n 8 129 | "^")))) 130 | (group-n 2 131 | "get") 132 | (group-n 2 133 | "set") 134 | (seq (group-n 2 135 | "pointer") 136 | (or (seq (* ,ws) 137 | (group-n 3 "*") 138 | (* ,ws)) 139 | (+ ,ws)) 140 | ,cid 141 | (opt (+ ,ws) 142 | (group-n 4 "as") 143 | (+ ,ws) 144 | ,hsid-type) 145 | (opt (+ ,ws) 146 | (group-n 5 147 | (or "foreign" 148 | "stable"))) 149 | (opt 150 | (or (seq (+ ,ws) 151 | (group-n 6 152 | "newtype")) 153 | (seq (* ,ws) 154 | "->" 155 | (* ,ws) 156 | ,hsid-type))) 157 | (opt (+ ,ws) 158 | (group-n 7 159 | "nocode"))) 160 | (group-n 2 161 | "class") 162 | (group-n 2 163 | "alignof") 164 | (group-n 2 165 | "offsetof") 166 | (seq (group-n 2 167 | "const") 168 | (+ ,ws) 169 | ,cid) 170 | (seq (group-n 2 171 | "typedef") 172 | (+ ,ws) 173 | ,cid 174 | (+ ,ws) 175 | ,hsid-type) 176 | (group-n 2 177 | "nonGNU") 178 | ;; TODO: default hook not implemented 179 | ) 180 | (* ,anychar) 181 | (group-n 9 "#}")))))) 182 | ;; Override highlighting for pairs in order to always distinguish them. 183 | (1 'haskell-c2hs-hook-pair-face t) 184 | (2 'haskell-c2hs-hook-name-face) 185 | ;; Make matches lax, i.e. do not signal error if nothing 186 | ;; matched. 187 | (3 'haskell-c2hs-hook-name-face nil t) 188 | (4 'haskell-c2hs-hook-name-face nil t) 189 | (5 'haskell-c2hs-hook-name-face nil t) 190 | (6 'haskell-c2hs-hook-name-face nil t) 191 | (7 'haskell-c2hs-hook-name-face nil t) 192 | (8 'font-lock-negation-char-face nil t) 193 | ;; Override highlighting for pairs in order to always distinguish them. 194 | (9 'haskell-c2hs-hook-pair-face t)) 195 | ,@(haskell-font-lock-keywords))) 196 | 197 | ;;;###autoload 198 | (define-derived-mode haskell-c2hs-mode haskell-mode "C2HS" 199 | "Mode for editing *.chs files of the c2hs haskell tool." 200 | (setq-local font-lock-defaults 201 | (cons 'haskell-c2hs-font-lock-keywords 202 | (cdr font-lock-defaults)))) 203 | 204 | 205 | (provide 'haskell-c2hs) 206 | 207 | ;; haskell-c2hs.el ends here 208 | -------------------------------------------------------------------------------- /haskell-collapse.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-collapse.el --- Collapse expressions -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | ;; Copyright (c) 2017 Vasantha Ganesh Kanniappan <vasanthaganesh.k@tuta.io>. 5 | 6 | ;; This file is free software; you can redistribute it and/or modify 7 | ;; it under the terms of the GNU General Public License as published by 8 | ;; the Free Software Foundation; either version 3, or (at your option) 9 | ;; any later version. 10 | 11 | ;; This file is distributed in the hope that it will be useful, 12 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ;; GNU General Public License for more details. 15 | 16 | ;; You should have received a copy of the GNU General Public License 17 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 18 | 19 | ;;; Code: 20 | 21 | (require 'hideshow) 22 | 23 | ;;; TODO: 24 | ;;; -> Make it work for braces 25 | 26 | (defun haskell-hide-toggle () 27 | "Toggle visibility of existing forms at point. " 28 | (interactive) 29 | (hs-minor-mode 1) 30 | (save-excursion 31 | (let* ((modified (buffer-modified-p)) 32 | (inhibit-read-only t) 33 | (position (haskell-indented-block)) 34 | (beg (car position)) 35 | (end (cdr position))) 36 | (if (and beg end) 37 | (if (overlays-in beg end) 38 | (hs-discard-overlays beg end) 39 | (hs-make-overlay beg end 'code))) 40 | (set-buffer-modified-p modified)))) 41 | 42 | (defun haskell-blank-line-p () 43 | "Returns `t' if line is empty or composed only of whitespace." 44 | (save-excursion 45 | (beginning-of-line) 46 | (= (line-end-position) 47 | (progn (skip-chars-forward "[:blank:]") (point))))) 48 | 49 | (defun haskell-indented-block () 50 | "return (start-of-indentation . end-of-indentation)" 51 | (let ((cur-indent (current-indentation)) 52 | (nxt-line-indent (haskell-next-line-indentation 1)) 53 | (prev-line-indent (haskell-next-line-indentation -1)) 54 | (beg-of-line (save-excursion (end-of-line) 55 | (point)))) 56 | (cond ((and (= cur-indent 0) 57 | (= nxt-line-indent 0)) nil) 58 | ((haskell-blank-line-p) nil) 59 | ((> nxt-line-indent cur-indent) 60 | (cons beg-of-line 61 | (haskell-find-line-with-indentation '> 1))) 62 | ((or (= nxt-line-indent cur-indent) 63 | (<= prev-line-indent cur-indent)) 64 | (cons (haskell-find-line-with-indentation '>= -1) 65 | (haskell-find-line-with-indentation '>= 1))) 66 | (t nil)))) 67 | 68 | (defun haskell-next-line-indentation (dir) 69 | "returns (integer) indentation of the next if dir=1, previous line 70 | indentation if dir=-1" 71 | (save-excursion 72 | (progn 73 | (while (and (zerop (forward-line dir)) 74 | (haskell-blank-line-p))) 75 | (current-indentation)))) 76 | 77 | (defun haskell-find-line-with-indentation (comparison direction) 78 | "comparison is >= or >, direction if 1 finds forward, if -1 finds backward" 79 | (save-excursion 80 | (let ((start-indent (current-indentation))) 81 | (progn 82 | (while (and (zerop (forward-line direction)) 83 | (or (haskell-blank-line-p) 84 | (funcall comparison (current-indentation) start-indent)))) 85 | (when (= direction 1) (forward-line -1)) 86 | (end-of-line) 87 | (point))))) 88 | 89 | (defun haskell-hide-toggle-all () 90 | "hides all top level functions" 91 | (interactive) 92 | (save-excursion 93 | (goto-char (point-max)) 94 | (while (zerop (forward-line -1)) 95 | (goto-char (line-beginning-position)) 96 | (when (= (current-indentation) 0) (haskell-hide-toggle))))) 97 | 98 | (defvar haskell-collapse-mode-map 99 | (let ((map (make-sparse-keymap))) 100 | (define-key map (kbd "C-c @ C-c") 'haskell-hide-toggle) 101 | (define-key map (kbd "C-c @ C-M-c") 'haskell-hide-toggle-all) 102 | (define-key map (kbd "C-c @ C-M-s") 'haskell-hide-toggle-all) 103 | (define-key map (kbd "C-c @ C-M-h") 'haskell-hide-toggle-all) 104 | map) 105 | "Keymap for using `haskell-collapse-mode'.") 106 | 107 | ;;;###autoload 108 | (define-minor-mode haskell-collapse-mode 109 | "Minor mode to collapse and expand haskell expressions" 110 | :init-value nil 111 | :lighter " Haskell-Collapse" 112 | :keymap haskell-collapse-mode-map) 113 | 114 | (provide 'haskell-collapse) 115 | -------------------------------------------------------------------------------- /haskell-complete-module.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-complete-module.el --- A fast way to complete Haskell module names -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'cl-lib) 21 | 22 | (defcustom haskell-complete-module-preferred 23 | '() 24 | "Override ordering of module results by specifying preferred modules." 25 | :group 'haskell 26 | :type '(repeat string)) 27 | 28 | (defcustom haskell-complete-module-max-display 29 | 10 30 | "Maximum items to display in minibuffer." 31 | :group 'haskell 32 | :type 'number) 33 | 34 | (defun haskell-complete-module-read (prompt candidates) 35 | "Interactively auto-complete from a list of candidates." 36 | (let ((stack (list)) 37 | (pattern "") 38 | (result nil)) 39 | (delete-dups candidates) 40 | (setq candidates 41 | (sort candidates 42 | (lambda (a b) 43 | (let ((a-mem (member a haskell-complete-module-preferred)) 44 | (b-mem (member b haskell-complete-module-preferred))) 45 | (cond 46 | ((and a-mem (not b-mem)) 47 | t) 48 | ((and b-mem (not a-mem)) 49 | nil) 50 | (t 51 | (string< a b))))))) 52 | (while (not result) 53 | (let ((key 54 | (key-description 55 | (vector 56 | (read-key 57 | (concat (propertize prompt 'face 'minibuffer-prompt) 58 | (propertize pattern 'face 'font-lock-type-face) 59 | "{" 60 | (mapconcat #'identity 61 | (let* ((i 0)) 62 | (cl-loop for candidate in candidates 63 | while (<= i haskell-complete-module-max-display) 64 | do (cl-incf i) 65 | collect (cond ((> i haskell-complete-module-max-display) 66 | "...") 67 | ((= i 1) 68 | (propertize candidate 'face 'ido-first-match-face)) 69 | (t candidate)))) 70 | " | ") 71 | "}")))))) 72 | (cond 73 | ((string= key "C-g") 74 | (keyboard-quit)) 75 | ((string= key "DEL") 76 | (unless (null stack) 77 | (setq candidates (pop stack))) 78 | (unless (string= "" pattern) 79 | (setq pattern (substring pattern 0 -1)))) 80 | ((string= key "RET") 81 | (setq result (or (car candidates) 82 | pattern))) 83 | ((string= key "<left>") 84 | (setq candidates 85 | (append (last candidates) 86 | (butlast candidates)))) 87 | ((string= key "<right>") 88 | (setq candidates 89 | (append (cdr candidates) 90 | (list (car candidates))))) 91 | (t 92 | (when (string-match "[A-Za-z0-9_'.]+" key) 93 | (push candidates stack) 94 | (setq pattern (concat pattern key)) 95 | (setq candidates (haskell-complete-module pattern candidates))))))) 96 | result)) 97 | 98 | (defun haskell-complete-module (pattern candidates) 99 | "Filter the CANDIDATES using PATTERN." 100 | (let ((case-fold-search t)) 101 | (cl-loop for candidate in candidates 102 | when (haskell-complete-module-match pattern candidate) 103 | collect candidate))) 104 | 105 | (defun haskell-complete-module-match (pattern text) 106 | "Match PATTERN against TEXT." 107 | (string-match (haskell-complete-module-regexp pattern) 108 | text)) 109 | 110 | (defun haskell-complete-module-regexp (pattern) 111 | "Make a regular expression for the given module pattern. Example: 112 | 113 | \"c.m.s\" -> \"^c[^.]*\\.m[^.]*\\.s[^.]*\" 114 | 115 | " 116 | (let ((components (mapcar #'haskell-complete-module-component 117 | (split-string pattern "\\." t)))) 118 | (concat "^" 119 | (mapconcat #'identity 120 | components 121 | "\\.")))) 122 | 123 | (defun haskell-complete-module-component (component) 124 | "Make a regular expression for the given component. Example: 125 | 126 | \"co\" -> \"c[^.]*o[^.]*\" 127 | 128 | " 129 | (replace-regexp-in-string "\\(.\\)" "\\1[^.]*" component)) 130 | 131 | (provide 'haskell-complete-module) 132 | -------------------------------------------------------------------------------- /haskell-hoogle.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-hoogle.el --- Look up Haskell documentation via hoogle -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright © 2015 Steve Purcell 4 | ;; 2016 Arthur Fayzrakhmanov 5 | 6 | ;; Author: Steve Purcell <steve@sanityinc.com> 7 | ;; Keywords: docs 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | ;;; Commentary: 23 | 24 | ;; Functions for looking up documentation with hoogle, via 25 | ;; either local or remote servers. 26 | 27 | ;;; Code: 28 | 29 | (require 'ansi-color) 30 | (require 'view) 31 | (require 'haskell-mode) 32 | (require 'haskell-utils) 33 | 34 | (defun hoogle-prompt () 35 | "Prompt for Hoogle query." 36 | (let ((def (haskell-ident-at-point))) 37 | (if (and def (symbolp def)) (setq def (symbol-name def))) 38 | (list (read-string (if def 39 | (format "Hoogle query (default %s): " def) 40 | "Hoogle query: ") 41 | nil nil def) 42 | ))) 43 | 44 | (defcustom haskell-hoogle-command 45 | (if (executable-find "hoogle") "hoogle") 46 | "Name of the command to use to query Hoogle. 47 | Can also be a function that returns the command as a string. 48 | If nil, use the Hoogle web-site." 49 | :group 'haskell 50 | :type '(choice (const :tag "Use Web-site" nil) 51 | string 52 | function)) 53 | 54 | (defcustom haskell-hoogle-url "https://hoogle.haskell.org/?hoogle=%s" 55 | "Default value for hoogle web site." 56 | :group 'haskell 57 | :type '(choice 58 | (const :tag "haskell-org" "https://hoogle.haskell.org/?hoogle=%s") 59 | (const :tag "fp-complete" "https://www.stackage.org/lts/hoogle?q=%s") 60 | string)) 61 | 62 | ;;;###autoload 63 | (defun haskell-hoogle (query &optional info) 64 | "Do a Hoogle search for QUERY. 65 | 66 | If prefix argument INFO is given, then `haskell-hoogle-command' 67 | is asked to show extra info for the items matching QUERY.." 68 | (interactive (append (hoogle-prompt) current-prefix-arg)) 69 | (if (null haskell-hoogle-command) 70 | (browse-url (format haskell-hoogle-url (url-hexify-string query))) 71 | (let* ((command (concat (if (functionp haskell-hoogle-command) 72 | (funcall haskell-hoogle-command) 73 | haskell-hoogle-command) 74 | (if info " -i " "") 75 | " --color " (shell-quote-argument query))) 76 | (output (shell-command-to-string command))) 77 | (with-help-window "*hoogle*" 78 | (with-current-buffer standard-output 79 | (let ((outs (ansi-color-filter-apply output))) 80 | (delay-mode-hooks (haskell-mode)) 81 | (if info 82 | (let ((lns (split-string output "\n" t " "))) 83 | (insert (car lns) "\n\n") 84 | (dolist (ln (cdr lns)) (insert "-- " ln "\n"))) 85 | (insert outs) 86 | (forward-line -1) 87 | (when (looking-at-p "^plus more results") (insert "\n-- "))) 88 | (view-mode))))))) 89 | 90 | ;;;###autoload 91 | (defalias 'hoogle 'haskell-hoogle) 92 | 93 | ;;;###autoload 94 | (defun haskell-hoogle-lookup-from-website (query) 95 | "Lookup QUERY at `haskell-hoogle-url'." 96 | (interactive (hoogle-prompt)) 97 | (browse-url (format haskell-hoogle-url (url-hexify-string query)))) 98 | 99 | (defcustom haskell-hoogle-server-command (lambda (port) 100 | (list "hoogle" "server" 101 | "--local" 102 | "-p" 103 | (number-to-string port))) 104 | "Command used to start the local hoogle server." 105 | :group 'haskell 106 | :type 'function 107 | ) 108 | 109 | (defvar haskell-hoogle-server-process-name "emacs-local-hoogle") 110 | (defvar haskell-hoogle-server-buffer-name (format "*%s*" haskell-hoogle-server-process-name)) 111 | (defvar haskell-hoogle-port-number 49513 "Port number.") 112 | (defvar haskell-hoogle-server-process nil "The process handle of the local hoogle server.") 113 | 114 | (defun haskell-hoogle-start-server () 115 | "Start hoogle local server." 116 | (interactive) 117 | (unless (haskell-hoogle-server-live-p) 118 | (set 'haskell-hoogle-server-process 119 | (apply 'start-process 120 | (append (list haskell-hoogle-server-process-name 121 | (get-buffer-create haskell-hoogle-server-buffer-name)) 122 | (funcall haskell-hoogle-server-command haskell-hoogle-port-number)))) 123 | ) 124 | ) 125 | 126 | (defun haskell-hoogle-server-live-p () 127 | "Whether the hoogle server process is live." 128 | (condition-case _err 129 | (process-live-p haskell-hoogle-server-process) 130 | (error nil))) 131 | 132 | (defun haskell-hoogle-kill-server () 133 | "Kill the hoogle server if it is live." 134 | (interactive) 135 | (when (haskell-hoogle-server-live-p) 136 | (kill-process (get-buffer-create haskell-hoogle-server-buffer-name)) 137 | (set 'haskell-hoogle-server-process nil))) 138 | 139 | ;;;###autoload 140 | (defun haskell-hoogle-lookup-from-local () 141 | "Lookup QUERY on local hoogle server." 142 | (interactive) 143 | (if (haskell-hoogle-server-live-p) 144 | (browse-url (format "http://localhost:%i/?hoogle=%s" 145 | haskell-hoogle-port-number 146 | (car (hoogle-prompt)))) 147 | (haskell-mode-toggle-interactive-prompt-state) 148 | (unwind-protect 149 | (when (y-or-n-p "Hoogle server not running, start hoogle server? ") 150 | (haskell-hoogle-start-server)) 151 | (haskell-mode-toggle-interactive-prompt-state t)))) 152 | 153 | (provide 'haskell-hoogle) 154 | ;;; haskell-hoogle.el ends here 155 | -------------------------------------------------------------------------------- /haskell-menu.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-menu.el --- A Haskell sessions menu -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2013 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;;; Todo: 27 | 28 | ;;; Code: 29 | 30 | (require 'cl-lib) 31 | (require 'haskell-session) 32 | (require 'haskell-process) 33 | (require 'haskell-interactive-mode) 34 | 35 | (defcustom haskell-menu-buffer-name "*haskell-menu*" 36 | "The name of the Haskell session menu buffer" 37 | :group 'haskell-interactive 38 | :type 'string) 39 | 40 | ;;;###autoload 41 | (defun haskell-menu () 42 | "Launch the Haskell sessions menu." 43 | (interactive) 44 | (or (get-buffer haskell-menu-buffer-name) 45 | (with-current-buffer (get-buffer-create haskell-menu-buffer-name) 46 | (haskell-menu-mode))) 47 | (switch-to-buffer-other-window (get-buffer haskell-menu-buffer-name)) 48 | (haskell-menu-revert-function nil nil)) 49 | 50 | (defvar haskell-menu-mode-map 51 | (let ((map (make-sparse-keymap))) 52 | (define-key map (kbd "n") 'next-line) 53 | (define-key map (kbd "p") 'previous-line) 54 | (define-key map (kbd "RET") 'haskell-menu-mode-ret) 55 | map) 56 | "Keymap for `haskell-menu-mode'.") 57 | 58 | (define-derived-mode haskell-menu-mode special-mode "Haskell Session Menu" 59 | "Major mode for managing Haskell sessions. 60 | Each line describes one session. 61 | Letters do not insert themselves; instead, they are commands." 62 | (setq buffer-read-only t) 63 | (setq-local revert-buffer-function 'haskell-menu-revert-function) 64 | (setq truncate-lines t) 65 | (haskell-menu-revert-function nil t)) 66 | 67 | (suppress-keymap haskell-menu-mode-map t) 68 | 69 | (defun haskell-menu-revert-function (_arg1 _arg2) 70 | "Function to refresh the display." 71 | (let ((buffer-read-only nil) 72 | (orig-line (line-number-at-pos)) 73 | (orig-col (current-column))) 74 | (or (eq buffer-undo-list t) 75 | (setq buffer-undo-list nil)) 76 | (erase-buffer) 77 | (haskell-menu-insert-menu) 78 | (goto-char (point-min)) 79 | (forward-line (1- orig-line)) 80 | (forward-char orig-col))) 81 | 82 | (defun haskell-menu-insert-menu () 83 | "Insert the Haskell sessions menu to the current buffer." 84 | (if (null haskell-sessions) 85 | (insert "No Haskell sessions.") 86 | (haskell-menu-tabulate 87 | (list "Name" "PID" "Time" "RSS" "Cabal directory" "Working directory" "Command") 88 | (mapcar (lambda (session) 89 | (let ((process (haskell-process-process (haskell-session-process session)))) 90 | (cond 91 | (process 92 | (let ((id (process-id process))) 93 | (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer) 94 | (if (process-live-p process) (number-to-string id) "-") 95 | (if (process-live-p process) 96 | (format-time-string "%H:%M:%S" 97 | (encode-time (cl-caddr (assoc 'etime (process-attributes id))) 98 | 0 0 0 0 0)) 99 | "-") 100 | (if (process-live-p process) 101 | (concat (number-to-string (/ (cdr (assoc 'rss (process-attributes id))) 102 | 1024)) 103 | "MB") 104 | "-") 105 | (haskell-session-cabal-dir session) 106 | (haskell-session-current-dir session) 107 | (mapconcat 'identity (process-command process) " ")))) 108 | (t (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer) 109 | "—" 110 | "—" 111 | "—" 112 | (haskell-session-cabal-dir session) 113 | (haskell-session-current-dir session)))))) 114 | haskell-sessions)))) 115 | 116 | (defun haskell-menu-tabulate (headings rows) 117 | "Prints a list of lists as a formatted table to the current buffer." 118 | (let* ((columns (length headings)) 119 | (widths (make-list columns 0))) 120 | ;; Calculate column widths. This is kind of hideous. 121 | (dolist (row rows) 122 | (setq widths 123 | (let ((list (list))) 124 | (dotimes (i columns) 125 | (setq list (cons (max (nth i widths) 126 | (1+ (length (nth i row))) 127 | (1+ (length (nth i headings)))) 128 | list))) 129 | (reverse list)))) 130 | ;; Print headings. 131 | (let ((heading (propertize " " 'display '(space :align-to 0)))) 132 | (dotimes (i columns) 133 | (setq heading (concat heading 134 | (format (concat "%-" (number-to-string (nth i widths)) "s") 135 | (nth i headings))))) 136 | (setq header-line-format heading)) 137 | ;; Print tabulated rows. 138 | (dolist (row rows) 139 | (dotimes (i columns) 140 | (insert (format (concat "%-" (number-to-string (nth i widths)) "s") 141 | (nth i row)))) 142 | (insert "\n")))) 143 | 144 | (defun haskell-menu-mode-ret () 145 | "Handle RET key." 146 | (interactive) 147 | (let* ((name (save-excursion 148 | (goto-char (line-beginning-position)) 149 | (buffer-substring-no-properties (point) 150 | (progn (search-forward " ") 151 | (forward-char -1) 152 | (point))))) 153 | (session (car (cl-remove-if-not (lambda (session) 154 | (string= (haskell-session-name session) 155 | name)) 156 | haskell-sessions)))) 157 | (switch-to-buffer (haskell-session-interactive-buffer session)))) 158 | 159 | (provide 'haskell-menu) 160 | 161 | ;;; haskell-menu.el ends here 162 | -------------------------------------------------------------------------------- /haskell-modules.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-modules.el --- -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'haskell-sort-imports) 21 | (require 'haskell-align-imports) 22 | (require 'haskell-session) 23 | (require 'haskell-navigate-imports) 24 | (require 'haskell-complete-module) 25 | (require 'haskell-sandbox) 26 | (require 'haskell-customize) 27 | 28 | (defun haskell-add-import (&optional module) 29 | "Add an import to the import list. Sorts and aligns imports, 30 | unless `haskell-stylish-on-save' is set, in which case we defer 31 | to stylish-haskell." 32 | (interactive) 33 | (save-excursion 34 | (goto-char (point-max)) 35 | (haskell-navigate-imports) 36 | (insert (haskell-import-for-module 37 | (or module 38 | (haskell-complete-module-read 39 | "Module: " 40 | (haskell-session-all-modules (haskell-modules-session)))))) 41 | (unless haskell-stylish-on-save (haskell-sort-imports) 42 | (haskell-align-imports)))) 43 | 44 | (defun haskell-import-for-module (module) 45 | "Get import statements for the given module." 46 | (let ((mapping (assoc module haskell-import-mapping))) 47 | (if mapping 48 | (cdr mapping) 49 | (concat (read-from-minibuffer "Import line: " 50 | (format "import %s" module)) 51 | "\n")))) 52 | 53 | ;;;###autoload 54 | (defun haskell-session-installed-modules (_session &optional _dontcreate) 55 | "Get the modules installed in the current package set." 56 | ;; TODO: Again, this makes HEAVY use of unix utilities. It'll work 57 | ;; fine in Linux, probably okay on OS X, and probably not at all on 58 | ;; Windows. Again, if someone wants to test on Windows and come up 59 | ;; with alternatives that's OK. 60 | ;; 61 | ;; Ideally all these package queries can be provided by a Haskell 62 | ;; program based on the Cabal API. Possibly as a nice service. Such 63 | ;; a service could cache and do nice things like that. For now, this 64 | ;; simple shell script takes us far. 65 | ;; 66 | ;; Probably also we can take the code from inferior-haskell-mode. 67 | ;; 68 | ;; Ugliness aside, if it saves us time to type it's a winner. 69 | ;; 70 | ;; FIXME/TODO: add support for (eq 'cabal-repl (haskell-process-type)) 71 | (let ((session (haskell-session-maybe))) 72 | (when session 73 | (let ((modules (shell-command-to-string 74 | (format "%s 2> /dev/null | %s | %s" 75 | (cond 76 | ((haskell-sandbox-exists-p session) 77 | (concat "ghc-pkg dump -f " 78 | (shell-quote-argument (haskell-sandbox-pkgdb session)))) 79 | (t "ghc-pkg dump")) 80 | "egrep '^(exposed-modules: | )[A-Z]'" 81 | "cut -c18-")))) 82 | (split-string modules))))) 83 | 84 | ;;;###autoload 85 | (defun haskell-session-all-modules (session &optional dontcreate) 86 | "Get all modules -- installed or in the current project. 87 | If DONTCREATE is non-nil don't create a new session." 88 | (append (haskell-session-installed-modules session dontcreate) 89 | (haskell-session-project-modules session dontcreate))) 90 | 91 | ;;;###autoload 92 | (defun haskell-session-project-modules (session &optional dontcreate) 93 | "Get the modules of the current project. 94 | If DONTCREATE is non-nil don't create a new session." 95 | (if (or (not dontcreate) (haskell-session-maybe)) 96 | (let* ((modules 97 | (shell-command-to-string 98 | (format "%s && %s" 99 | (format "cd %s" (haskell-session-cabal-dir session)) 100 | ;; TODO: Use a different, better source. Possibly hasktags or some such. 101 | ;; TODO: At least make it cross-platform. Linux 102 | ;; (and possibly OS X) have egrep, Windows 103 | ;; doesn't -- or does it via Cygwin or MinGW? 104 | ;; This also doesn't handle module\nName. But those gits can just cut it out! 105 | "egrep '^module[\t\r ]+[^(\t\r ]+' . -r -I --include='*.*hs' --include='*.hsc' -s -o -h | sed 's/^module[\t\r ]*//' | sort | uniq")))) 106 | (split-string modules)))) 107 | 108 | (defun haskell-modules-session () 109 | "Get the `haskell-session', throw an error if it's not 110 | available." 111 | (or (haskell-session-maybe) 112 | (haskell-session-assign 113 | (or (haskell-session-from-buffer) 114 | (haskell-session-choose) 115 | (error "No session associated with this buffer. Try M-x haskell-session-change or report this as a bug."))))) 116 | 117 | (provide 'haskell-modules) 118 | -------------------------------------------------------------------------------- /haskell-move-nested.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-move-nested.el --- Change the column of text nested below a line -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2010 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This program is free software: you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License as 11 | ;; published by the Free Software Foundation, either version 3 of 12 | ;; the License, or (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be 15 | ;; useful, but WITHOUT ANY WARRANTY; without even the implied 16 | ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 17 | ;; PURPOSE. See the GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public 20 | ;; License along with this program. If not, see 21 | ;; <http://www.gnu.org/licenses/>. 22 | 23 | ;;; Commentary: 24 | 25 | ;; This module is intended for Haskell mode users, but is 26 | ;; independent of Haskell mode. 27 | 28 | ;; Example usage: 29 | 30 | ;; (define-key haskell-mode-map (kbd "C-,") 'haskell-move-nested-left) 31 | ;; (define-key haskell-mode-map (kbd "C-.") 'haskell-move-nested-right) 32 | 33 | ;;; Code: 34 | 35 | ;;;###autoload 36 | (defun haskell-move-nested (cols) 37 | "Shift the nested off-side-rule block adjacent to point. 38 | It shift the nested off-side-rule block adjacent to point by COLS 39 | columns to the right. 40 | 41 | In Transient Mark mode, if the mark is active, operate on the contents 42 | of the region instead. 43 | " 44 | (save-excursion 45 | (if (and transient-mark-mode mark-active) 46 | (progn 47 | (indent-rigidly (region-beginning) (region-end) cols) 48 | (setq deactivate-mark nil)) 49 | (let ((region (haskell-move-nested-region))) 50 | (when region 51 | (indent-rigidly (car region) (cdr region) cols)))))) 52 | 53 | ;;;###autoload 54 | (defun haskell-move-nested-right (cols) 55 | "Increase indentation of the following off-side-rule block adjacent to point. 56 | 57 | Use a numeric prefix argument to indicate amount of indentation to apply. 58 | 59 | In Transient Mark mode, if the mark is active, operate on the contents 60 | of the region instead." 61 | (interactive "p") 62 | (haskell-move-nested cols)) 63 | 64 | ;;;###autoload 65 | (defun haskell-move-nested-left (cols) 66 | "Decrease indentation of the following off-side-rule block adjacent to point. 67 | 68 | Use a numeric prefix argument to indicate amount of indentation to apply. 69 | 70 | In Transient Mark mode, if the mark is active, operate on the contents 71 | of the region instead." 72 | (interactive "p") 73 | (haskell-move-nested (- cols))) 74 | 75 | (defun haskell-move-nested-region () 76 | "Infer region off-side-rule block adjacent to point. 77 | Used by `haskell-move-nested'. 78 | " 79 | (save-excursion 80 | (let ((starting-level (current-column))) 81 | (forward-line) 82 | (let ((current-level (haskell-move-nested-indent-level))) 83 | (let ((start-point (line-beginning-position)) 84 | (start-end-point (line-end-position)) 85 | (end-point nil) 86 | (last-line 0)) 87 | (forward-line) 88 | (while (and (not (= (line-beginning-position) last-line)) 89 | (or (> (haskell-move-nested-indent-level) starting-level) 90 | (and (> current-level starting-level) 91 | (>= (haskell-move-nested-indent-level) current-level)))) 92 | (setq last-line (line-beginning-position)) 93 | (setq end-point (line-end-position)) 94 | (forward-line)) 95 | (cons start-point (or end-point 96 | start-end-point))))))) 97 | 98 | (defun haskell-move-nested-indent-level () 99 | (max 100 | 0 101 | (1- (length 102 | (buffer-substring-no-properties 103 | (line-beginning-position) 104 | (or (save-excursion (goto-char (line-beginning-position)) 105 | (search-forward-regexp "[^ ]" (line-end-position) t 1)) 106 | (line-beginning-position))))))) 107 | 108 | (defun haskell-kill-nested () 109 | "Kill the nested region after point." 110 | (interactive) 111 | (let ((start (point)) 112 | (reg (save-excursion 113 | (search-backward-regexp "^[ ]+" (line-beginning-position) t 1) 114 | (search-forward-regexp "[^ ]" (line-end-position) t 1) 115 | (haskell-move-nested-region)))) 116 | (kill-region start (cdr reg)))) 117 | 118 | (defun haskell-delete-nested () 119 | "Kill the nested region after point." 120 | (interactive) 121 | (let ((start (point)) 122 | (reg (save-excursion 123 | (search-backward-regexp "^[ ]+" (line-beginning-position) t 1) 124 | (search-forward-regexp "[^ ]" (line-end-position) t 1) 125 | (haskell-move-nested-region)))) 126 | (delete-region start (cdr reg)))) 127 | 128 | (provide 'haskell-move-nested) 129 | 130 | ;;; haskell-move-nested.el ends here 131 | -------------------------------------------------------------------------------- /haskell-navigate-imports.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-navigate-imports.el --- A function for cycling through Haskell import lists -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2010 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This program is free software: you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License as 11 | ;; published by the Free Software Foundation, either version 3 of 12 | ;; the License, or (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be 15 | ;; useful, but WITHOUT ANY WARRANTY; without even the implied 16 | ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 17 | ;; PURPOSE. See the GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public 20 | ;; License along with this program. If not, see 21 | ;; <http://www.gnu.org/licenses/>. 22 | 23 | ;;; Commentary: 24 | 25 | ;; The cycling step will stop once at the last import list so 26 | ;; that it is easy to add a new import list. 27 | 28 | ;; This module works completely independently of any libraries 29 | ;; (including haskell-mode). 30 | 31 | ;; Exports three interactive functions: 32 | ;; 1. haskell-navigate-imports 33 | ;; 2. haskell-navigate-imports-go 34 | ;; 3. haskell-navigate-imports-return 35 | 36 | ;; Example usage: 37 | 38 | ;; (require 'haskell-navigate-imports) 39 | ;; (define-key haskell-mode-map (kbd "<f8>") 'haskell-navigate-imports) 40 | 41 | ;;; Code: 42 | 43 | (defvar haskell-navigate-imports-start-point nil) 44 | 45 | (defvar haskell-literate) ; defined in haskell-mode.el 46 | 47 | ;;;###autoload 48 | (defun haskell-navigate-imports (&optional return) 49 | "Cycle the Haskell import lines or return to point (with prefix arg)." 50 | (interactive "P") 51 | (if return 52 | (haskell-navigate-imports-return) 53 | (haskell-navigate-imports-go))) 54 | 55 | ;;;###autoload 56 | (defun haskell-navigate-imports-go () 57 | "Go to the first line of a list of consecutive import lines. Cycles." 58 | (interactive) 59 | (unless (or (haskell-navigate-imports-line) 60 | (equal (line-beginning-position) (point-min)) 61 | (save-excursion (forward-line -1) 62 | (haskell-navigate-imports-line))) 63 | (setq haskell-navigate-imports-start-point (point))) 64 | (haskell-navigate-imports-go-internal)) 65 | 66 | ;;;###autoload 67 | (defun haskell-navigate-imports-return () 68 | "Return to the non-import point we were at before going to the module list. 69 | If we were originally at an import list, we can just cycle through easily." 70 | (interactive) 71 | (when haskell-navigate-imports-start-point 72 | (goto-char haskell-navigate-imports-start-point))) 73 | 74 | (defun haskell-navigate-imports-go-internal () 75 | "Go to the first line of a list of consecutive import lines. Cycle." 76 | (if (haskell-navigate-imports-line) 77 | (progn (haskell-navigate-imports-goto-end) 78 | (when (haskell-navigate-imports-find-forward-line) 79 | (haskell-navigate-imports-go-internal))) 80 | (let ((point (haskell-navigate-imports-find-forward-line))) 81 | (if point 82 | (goto-char point) 83 | (progn (goto-char (point-min)) 84 | (if (haskell-navigate-imports-find-forward-line) 85 | (haskell-navigate-imports-go-internal) 86 | (let ((module (if (eq haskell-literate 'bird) 87 | "^> ?module" 88 | "^module"))) 89 | (when (search-forward-regexp module nil t 1) 90 | (search-forward "\n\n" nil t 1))))))))) 91 | 92 | (defun haskell-navigate-imports-goto-end () 93 | "Skip a bunch of consecutive import lines." 94 | (while (not (or (equal (point) 95 | (point-max)) 96 | (not (haskell-navigate-imports-line)))) 97 | (forward-line))) 98 | 99 | (defun haskell-navigate-imports-find-forward-line () 100 | "Return a point with at an import line, or nothing." 101 | (save-excursion 102 | (while (not (or (equal (point) (point-max)) 103 | (haskell-navigate-imports-after-imports-p) ;; This one just speeds it up. 104 | (haskell-navigate-imports-line))) 105 | (forward-line)) 106 | (if (haskell-navigate-imports-line) 107 | (point) 108 | nil))) 109 | 110 | (defun haskell-navigate-imports-line () 111 | "Try to match the current line as a regexp." 112 | (let ((line (buffer-substring-no-properties (line-beginning-position) 113 | (line-end-position))) 114 | (import (if (eq haskell-literate 'bird) 115 | "^> ?import " 116 | "^import "))) 117 | (if (string-match import line) 118 | line 119 | nil))) 120 | 121 | (defun haskell-navigate-imports-after-imports-p () 122 | "Are we after the imports list? Just for a speed boost." 123 | (save-excursion 124 | (goto-char (line-beginning-position)) 125 | (not (not (search-forward-regexp "\\( = \\|\\<instance\\>\\| :: \\)" 126 | (line-end-position) t 1))))) 127 | 128 | (provide 'haskell-navigate-imports) 129 | 130 | ;;; haskell-navigate-imports.el ends here 131 | -------------------------------------------------------------------------------- /haskell-presentation-mode.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-presentation-mode.el --- Presenting Haskell things -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2013 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;;; Code: 27 | 28 | (require 'haskell-mode) 29 | (require 'haskell-session) 30 | 31 | (defvar haskell-presentation-mode-map 32 | (let ((map (make-sparse-keymap))) 33 | (define-key map (kbd "q") 'quit-window) 34 | (define-key map (kbd "c") 'haskell-presentation-clear) 35 | map) 36 | "Keymap for `haskell-presentation-mode'.") 37 | 38 | (define-derived-mode haskell-presentation-mode 39 | haskell-mode "Presentation" 40 | "Major mode for viewing Haskell snippets. 41 | \\{hypertext-mode-map}" 42 | (setq case-fold-search nil)) 43 | 44 | (defconst haskell-presentation-buffer-name 45 | "*Haskell Presentation*" 46 | "Haskell Presentation buffer name.") 47 | 48 | (defconst haskell-presentation-hint-message 49 | "-- Hit `q' to close this window; `c' to clear.\n\n" 50 | "Hint message appered in Haskell Presentation buffer.") 51 | 52 | (defun haskell-presentation-buffer () 53 | "Return Haskell Presentaion buffer. 54 | Return current presenation buffer or create new one if absent. 55 | Never returns nil." 56 | ;; TODO Provide interactive calling options: when called interactively make 57 | ;; the presentation buffer current. 58 | (let ((may-buffer (get-buffer haskell-presentation-buffer-name))) 59 | (if may-buffer 60 | may-buffer 61 | (let ((buffer (generate-new-buffer haskell-presentation-buffer-name))) 62 | (with-current-buffer buffer 63 | (insert haskell-presentation-hint-message) 64 | (haskell-presentation-mode) 65 | (setq buffer-read-only t)) 66 | buffer)))) 67 | 68 | (defun haskell-presentation-clear () 69 | "Clear Haskell Presentation buffer." 70 | (interactive) 71 | (let ((hp-buf (get-buffer haskell-presentation-buffer-name))) 72 | (when hp-buf 73 | (with-current-buffer hp-buf 74 | (let ((buffer-read-only nil)) 75 | (erase-buffer) 76 | (insert haskell-presentation-hint-message)))))) 77 | 78 | (defun haskell-presentation-present (session code &optional clear) 79 | "Present given code in a popup buffer. 80 | Creates temporal Haskell Presentation buffer and assigns it to 81 | given haskell SESSION; presented CODE will be fontified as 82 | haskell code. Give an optional non-nil CLEAR arg to clear the 83 | buffer before presenting message." 84 | (let ((buffer (haskell-presentation-buffer))) 85 | (with-current-buffer buffer 86 | 87 | (when (boundp 'shm-display-quarantine) 88 | (setq-local shm-display-quarantine nil)) 89 | 90 | (when clear (haskell-presentation-clear)) 91 | (haskell-session-assign session) 92 | (goto-char (point-min)) 93 | (forward-line 2) 94 | (save-excursion 95 | (let ((buffer-read-only nil)) 96 | (insert code "\n\n")))) 97 | 98 | (if (eq major-mode 'haskell-presentation-mode) 99 | (switch-to-buffer buffer) 100 | (pop-to-buffer buffer)))) 101 | 102 | (provide 'haskell-presentation-mode) 103 | 104 | ;;; haskell-presentation-mode.el ends here 105 | -------------------------------------------------------------------------------- /haskell-repl.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-repl.el --- REPL evaluation -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'cl-lib) 21 | (require 'haskell-interactive-mode) 22 | (require 'haskell-collapse) 23 | (require 'haskell-svg) 24 | 25 | (defun haskell-interactive-handle-expr () 26 | "Handle an inputted expression at the REPL." 27 | (let ((expr (haskell-interactive-mode-input))) 28 | (if (string= "" (replace-regexp-in-string " " "" expr)) 29 | ;; Just make a new prompt on space-only input 30 | (progn 31 | (goto-char (point-max)) 32 | (insert "\n") 33 | (haskell-interactive-mode-prompt)) 34 | (when (haskell-interactive-at-prompt) 35 | (cond 36 | ;; If already evaluating, then the user is trying to send 37 | ;; input to the REPL during evaluation. Most likely in 38 | ;; response to a getLine-like function. 39 | ((and (haskell-process-evaluating-p (haskell-interactive-process)) 40 | (= (line-end-position) (point-max))) 41 | (goto-char (point-max)) 42 | (let ((process (haskell-interactive-process)) 43 | (string (buffer-substring-no-properties 44 | haskell-interactive-mode-result-end 45 | (point)))) 46 | ;; here we need to go to end of line again as evil-mode 47 | ;; might have managed to put us one char back 48 | (goto-char (point-max)) 49 | (insert "\n") 50 | ;; Bring the marker forward 51 | (setq haskell-interactive-mode-result-end 52 | (point-max)) 53 | (haskell-process-set-sent-stdin process t) 54 | (haskell-process-send-string process string))) 55 | ;; Otherwise we start a normal evaluation call. 56 | (t (setq haskell-interactive-mode-old-prompt-start 57 | (copy-marker haskell-interactive-mode-prompt-start)) 58 | (set-marker haskell-interactive-mode-prompt-start (point-max)) 59 | (haskell-interactive-mode-history-add expr) 60 | (haskell-interactive-mode-do-expr expr))))))) 61 | 62 | (defun haskell-interactive-mode-do-expr (expr) 63 | (cond 64 | ((string-match "^:present " expr) 65 | (haskell-interactive-mode-do-presentation (replace-regexp-in-string "^:present " "" expr))) 66 | (t 67 | (haskell-interactive-mode-run-expr expr)))) 68 | 69 | (defun haskell-interactive-mode-run-expr (expr) 70 | "Run the given expression." 71 | (let ((session (haskell-interactive-session)) 72 | (process (haskell-interactive-process))) 73 | (with-current-buffer (haskell-session-interactive-buffer session) 74 | (haskell-process-queue-command 75 | process 76 | (make-haskell-command 77 | :state (list session process expr 0) 78 | :go (lambda (state) 79 | (goto-char (point-max)) 80 | (insert "\n") 81 | (setq haskell-interactive-mode-result-end 82 | (point-max)) 83 | (haskell-process-send-string (cadr state) 84 | (haskell-interactive-mode-multi-line (cl-caddr state))) 85 | (haskell-process-set-evaluating (cadr state) t)) 86 | :live (lambda (state buffer) 87 | (unless (and (string-prefix-p ":q" (cl-caddr state)) 88 | (string-prefix-p (cl-caddr state) ":quit")) 89 | (let* ((cursor (cl-cadddr state)) 90 | (next (replace-regexp-in-string 91 | haskell-process-prompt-regex 92 | "" 93 | (substring buffer cursor)))) 94 | (haskell-interactive-mode-eval-result (car state) next) 95 | (setf (cl-cdddr state) (list (length buffer))) 96 | nil))) 97 | :complete 98 | (lambda (state response) 99 | (haskell-process-set-evaluating (cadr state) nil) 100 | (unless (haskell-interactive-mode-trigger-compile-error state response) 101 | (haskell-interactive-mode-expr-result state response)))))))) 102 | 103 | (defun haskell-interactive-mode-expr-result (state response) 104 | "Print the result of evaluating the expression." 105 | (let ((response 106 | (with-temp-buffer 107 | (insert response) 108 | (haskell-interactive-mode-handle-h) 109 | (buffer-string)))) 110 | (when haskell-interactive-mode-eval-mode 111 | (unless (haskell-process-sent-stdin-p (cadr state)) 112 | (haskell-interactive-mode-eval-as-mode (car state) response)))) 113 | (haskell-interactive-mode-prompt (car state))) 114 | 115 | (defun haskell-interactive-mode-eval-as-mode (session text) 116 | "Insert TEXT font-locked according to `haskell-interactive-mode-eval-mode'." 117 | (with-current-buffer (haskell-session-interactive-buffer session) 118 | (let ((inhibit-read-only t)) 119 | (delete-region (1+ haskell-interactive-mode-prompt-start) (point)) 120 | (goto-char (point-max)) 121 | (insert (haskell-fontify-as-mode (haskell-svg-maybe-render-images text) 122 | haskell-interactive-mode-eval-mode)) 123 | (when haskell-interactive-mode-collapse 124 | (haskell-hide-toggle))))) 125 | 126 | (provide 'haskell-repl) 127 | -------------------------------------------------------------------------------- /haskell-sandbox.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-sandbox.el --- Support for sandboxes -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'cl-lib) 21 | (require 'haskell-session) 22 | 23 | (defun haskell-sandbox-path (session) 24 | "If there is a haskell-session, return the path to the usual sandbox location." 25 | (concat (haskell-session-cabal-dir session) 26 | "/.cabal-sandbox")) 27 | 28 | (defun haskell-sandbox-exists-p (session) 29 | "Is there a cabal sandbox?" 30 | (file-exists-p (haskell-sandbox-path session))) 31 | 32 | (defun haskell-sandbox-pkgdb (session) 33 | "Get the package database of the sandbox." 34 | (let* ((files (directory-files (haskell-sandbox-path session))) 35 | (dir (car (cl-remove-if-not (lambda (file) 36 | (string-match ".conf.d$" file)) 37 | files)))) 38 | (when dir 39 | (concat (haskell-sandbox-path session) "/" dir)))) 40 | 41 | (provide 'haskell-sandbox) 42 | -------------------------------------------------------------------------------- /haskell-session.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-session.el --- Haskell sessions -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2011-2012 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;;; Todo: 27 | 28 | ;;; Code: 29 | 30 | (require 'cl-lib) 31 | (require 'haskell-cabal) 32 | (require 'haskell-customize) 33 | 34 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 35 | ;; Globals 36 | 37 | ;; Used internally 38 | (defvar-local haskell-session nil) 39 | 40 | (defvar haskell-sessions (list) 41 | "All Haskell sessions in the Emacs session.") 42 | 43 | (defun haskell-session-tags-filename (session) 44 | "Get the filename for the TAGS file." 45 | (concat (haskell-session-cabal-dir session) "/TAGS")) 46 | 47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 48 | ;; Finding/clearing the session 49 | 50 | ;;;###autoload 51 | (defun haskell-session-maybe () 52 | "Maybe get the Haskell session, return nil if there isn't one." 53 | (if (default-boundp 'haskell-session) 54 | haskell-session 55 | (setq haskell-session nil))) 56 | 57 | (defun haskell-session-from-buffer () 58 | "Get the session based on the buffer." 59 | (when (and (buffer-file-name) 60 | (consp haskell-sessions)) 61 | (cl-reduce (lambda (acc a) 62 | (let ((dir (haskell-session-get a 'cabal-dir))) 63 | (if dir 64 | (if (string-prefix-p dir 65 | (file-name-directory (buffer-file-name))) 66 | (if acc 67 | (if (and 68 | (> (length (haskell-session-get a 'cabal-dir)) 69 | (length (haskell-session-get acc 'cabal-dir)))) 70 | a 71 | acc) 72 | a) 73 | acc) 74 | acc))) 75 | haskell-sessions 76 | :initial-value nil))) 77 | 78 | (defun haskell-session-default-name () 79 | "Generate a default project name for the new project prompt." 80 | (let ((file (haskell-cabal-find-file))) 81 | (or (when file 82 | (downcase (file-name-sans-extension 83 | (file-name-nondirectory file)))) 84 | "haskell"))) 85 | 86 | (defun haskell-session-assign (session) 87 | "Assing current buffer to SESSION. 88 | 89 | This could be helpful for temporary or auxiliary buffers such as 90 | presentation mode buffers (e.g. in case when session is killed 91 | with all relevant buffers)." 92 | (setq-local haskell-session session)) 93 | 94 | (defun haskell-session-choose () 95 | "Find a session by choosing from a list of the current sessions." 96 | (when haskell-sessions 97 | (let* ((session-name (funcall haskell-completing-read-function 98 | "Choose Haskell session: " 99 | (cl-remove-if (lambda (name) 100 | (and haskell-session 101 | (string= (haskell-session-name haskell-session) 102 | name))) 103 | (mapcar 'haskell-session-name haskell-sessions)))) 104 | (session (cl-find-if (lambda (session) 105 | (string= (haskell-session-name session) 106 | session-name)) 107 | haskell-sessions))) 108 | session))) 109 | 110 | (defun haskell-session-clear () 111 | "Clear the buffer of any Haskell session choice." 112 | (setq-local haskell-session nil)) 113 | 114 | (defun haskell-session-lookup (name) 115 | "Get the session by name." 116 | (cl-remove-if-not (lambda (s) 117 | (string= name (haskell-session-name s))) 118 | haskell-sessions)) 119 | 120 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 121 | ;; Session modules 122 | 123 | (defun haskell-session-strip-dir (session file) 124 | "Strip the load dir from the file path." 125 | (let ((cur-dir (haskell-session-current-dir session))) 126 | (if (> (length file) (length cur-dir)) 127 | (if (string= (substring file 0 (length cur-dir)) 128 | cur-dir) 129 | (replace-regexp-in-string 130 | "^[/\\]" "" 131 | (substring file 132 | (length cur-dir))) 133 | file) 134 | file))) 135 | 136 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 137 | ;; Accessing the session 138 | 139 | (defun haskell-session-current-dir (s) 140 | "Get the session current directory." 141 | (let ((dir (haskell-session-get s 'current-dir))) 142 | (or dir 143 | (error "No current directory.")))) 144 | 145 | (defun haskell-session-name (s) 146 | "Get the session name." 147 | (haskell-session-get s 'name)) 148 | 149 | (defun haskell-session-target (s) 150 | "Get the session build target. 151 | If `haskell-process-load-or-reload-prompt' is nil, accept `default'." 152 | (let* ((maybe-target (haskell-session-get s 'target)) 153 | (target (if maybe-target maybe-target 154 | (let ((new-target 155 | (if haskell-process-load-or-reload-prompt 156 | (haskell-session-choose-target "Build target (empty for default): " t) 157 | ""))) 158 | (haskell-session-set-target s new-target))))) 159 | (if (not (string= target "")) target nil))) 160 | 161 | (defun haskell-session-choose-target (&optional prompt blank-default history) 162 | "Ask the user which of the available targets they want to use. 163 | Optional arguments: 164 | 165 | PROMPT allows you to specify which prompt should be presented to the user. 166 | 167 | BLANK-DEFAULT will allow specifying a default blank argument. 168 | 169 | HISTORY provides the history to `completing-read'." 170 | (let ((prompt (or prompt "Build Target: ")) 171 | (known-targets (haskell-session-get-targets (haskell-process-type))) 172 | (default-target (when blank-default (list "")))) 173 | (completing-read prompt 174 | (append default-target known-targets) 175 | nil 176 | nil 177 | nil 178 | history 179 | default-target))) 180 | 181 | (defun haskell-session-get-targets (process-type) 182 | "Return a list of available targets." 183 | (cl-case process-type 184 | (stack-ghci 185 | (haskell-session-get-targets-command haskell-process-path-stack "ide" "targets")) 186 | (t 187 | (haskell-cabal-enum-targets (haskell-process-type))))) 188 | 189 | (defun haskell-session-get-targets-command (command &rest args) 190 | "Run an external command to obtain a list of available targets." 191 | (with-temp-buffer 192 | (cl-case (apply #'process-file command nil (current-buffer) t args) 193 | (0 194 | (cl-remove-if 195 | (lambda (line) 196 | (string= "" line)) 197 | (split-string (buffer-string)))) 198 | (1 nil)))) 199 | 200 | (defun haskell-session-set-target (s target) 201 | "Set the session build target." 202 | (haskell-session-set s 'target target)) 203 | 204 | (defun haskell-session-set-interactive-buffer (s v) 205 | "Set the session interactive buffer." 206 | (haskell-session-set s 'interactive-buffer v)) 207 | 208 | (defun haskell-session-set-process (s v) 209 | "Set the session process." 210 | (haskell-session-set s 'process v)) 211 | 212 | ;;;###autoload 213 | (defun haskell-session-process (s) 214 | "Get the session process." 215 | (haskell-session-get s 'process)) 216 | 217 | (defun haskell-session-set-cabal-dir (s v) 218 | "Set the session cabal-dir." 219 | (let ((true-path (file-truename v))) 220 | (haskell-session-set s 'cabal-dir true-path) 221 | (haskell-session-set-cabal-checksum s true-path))) 222 | 223 | (defun haskell-session-set-current-dir (s v) 224 | "Set the session current directory." 225 | (let ((true-path (file-truename v))) 226 | (haskell-session-set s 'current-dir true-path))) 227 | 228 | (defun haskell-session-set-cabal-checksum (s cabal-dir) 229 | "Set the session checksum of .cabal files" 230 | (haskell-session-set s 'cabal-checksum 231 | (haskell-cabal-compute-checksum cabal-dir))) 232 | 233 | (defun haskell-session-cabal-dir (s) 234 | "Get the session cabal-dir." 235 | (or (haskell-session-get s 'cabal-dir) 236 | (let ((set-dir (haskell-cabal-get-dir (not haskell-process-load-or-reload-prompt)))) 237 | (if set-dir 238 | (progn (haskell-session-set-cabal-dir s set-dir) 239 | set-dir) 240 | (haskell-session-cabal-dir s))))) 241 | 242 | (defun haskell-session-modify (session key update) 243 | "Update the value at KEY in SESSION with UPDATE." 244 | (haskell-session-set 245 | session 246 | key 247 | (funcall update 248 | (haskell-session-get session key)))) 249 | 250 | (defun haskell-session-get (session key) 251 | "Get the SESSION's KEY value. 252 | Returns nil if KEY not set." 253 | (cdr (assq key session))) 254 | 255 | (defun haskell-session-set (session key value) 256 | "Set the SESSION's KEY to VALUE. 257 | Returns newly set VALUE." 258 | (let ((cell (assq key session))) 259 | (if cell 260 | (setcdr cell value) ; modify cell in-place 261 | (setcdr session (cons (cons key value) (cdr session))) ; new cell 262 | value))) 263 | 264 | (provide 'haskell-session) 265 | 266 | ;;; haskell-session.el ends here 267 | -------------------------------------------------------------------------------- /haskell-sort-imports.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-sort-imports.el --- Sort the list of Haskell imports at the point alphabetically -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2010 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This program is free software: you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License as 11 | ;; published by the Free Software Foundation, either version 3 of 12 | ;; the License, or (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be 15 | ;; useful, but WITHOUT ANY WARRANTY; without even the implied 16 | ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 17 | ;; PURPOSE. See the GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public 20 | ;; License along with this program. If not, see 21 | ;; <http://www.gnu.org/licenses/>. 22 | 23 | ;;; Commentary: 24 | 25 | ;; If the region is active it sorts the imports within the 26 | ;; region. 27 | 28 | ;; This will align and sort the columns of the current import 29 | ;; list. It's more or less the coolest thing on the planet. 30 | 31 | ;;; Code: 32 | 33 | (require 'cl-lib) 34 | 35 | (defconst haskell-sort-imports-regexp 36 | (rx (seq bol 37 | "import" 38 | (+ space) 39 | (optional (group "qualified" space)) 40 | (optional (seq (* space) (group (char ?\") (+ (not (any ?\"))) (char ?\") space))) 41 | (* space) 42 | (group (+? (or (syntax word) (syntax symbol))) (* nonl))))) 43 | 44 | ;;;###autoload 45 | (defun haskell-sort-imports () 46 | "Sort the import list at point. It sorts the current group 47 | i.e. an import list separated by blank lines on either side. 48 | 49 | If the region is active, it will restrict the imports to sort 50 | within that region." 51 | (interactive) 52 | (when (haskell-sort-imports-at-import) 53 | (let* ((points (haskell-sort-imports-decl-points)) 54 | (current-string (buffer-substring-no-properties (car points) 55 | (cdr points))) 56 | (current-offset (- (point) (car points)))) 57 | (if (region-active-p) 58 | (progn (goto-char (region-beginning)) 59 | (haskell-sort-imports-goto-import-start)) 60 | (haskell-sort-imports-goto-group-start)) 61 | (let* ((start (point)) 62 | (imports (haskell-sort-imports-collect-imports)) 63 | (sorted (sort (cl-copy-list imports) 64 | (lambda (a b) 65 | (string< (haskell-sort-imports-normalize a) 66 | (haskell-sort-imports-normalize b)))))) 67 | (when (not (equal imports sorted)) 68 | (delete-region start (point)) 69 | (mapc (lambda (import) (insert import "\n")) sorted)) 70 | (goto-char start) 71 | (when (search-forward current-string nil t 1) 72 | (forward-char (- (length current-string))) 73 | (forward-char current-offset)))))) 74 | 75 | (defun haskell-sort-imports-normalize (i) 76 | "Normalize an import, if possible, so that it can be sorted." 77 | (if (string-match haskell-sort-imports-regexp 78 | i) 79 | (match-string 3 i) 80 | i)) 81 | 82 | (defun haskell-sort-imports-collect-imports () 83 | (let ((imports (list))) 84 | (while (looking-at "import") 85 | (let* ((points (haskell-sort-imports-decl-points)) 86 | (string (buffer-substring-no-properties (car points) 87 | (cdr points)))) 88 | (goto-char (min (1+ (cdr points)) 89 | (point-max))) 90 | (setq imports (cons string imports)))) 91 | (reverse (delq nil (delete-dups imports))))) 92 | 93 | (defun haskell-sort-imports-goto-group-start () 94 | "Go to the start of the import group." 95 | (or (and (search-backward "\n\n" nil t 1) 96 | (goto-char (+ 2 (line-end-position)))) 97 | (when (search-backward-regexp "^module " nil t 1) 98 | (goto-char (1+ (line-end-position)))) 99 | (goto-char (point-min)))) 100 | 101 | (defun haskell-sort-imports-at-import () 102 | "Are we at an import?" 103 | (save-excursion 104 | (haskell-sort-imports-goto-import-start) 105 | (looking-at "import"))) 106 | 107 | (defun haskell-sort-imports-goto-import-start () 108 | "Go to the start of the import." 109 | (goto-char (car (haskell-sort-imports-decl-points)))) 110 | 111 | (defun haskell-sort-imports-decl-points () 112 | "Get the points of the declaration." 113 | (save-excursion 114 | (let ((start (or (progn (goto-char (line-end-position)) 115 | (search-backward-regexp "^[^ \n]" nil t 1) 116 | (unless (or (looking-at "^-}$") 117 | (looking-at "^{-$")) 118 | (point))) 119 | 0)) 120 | (end (progn (goto-char (1+ (point))) 121 | (or (when (search-forward-regexp "[\n]+[^ \n]" nil t 1) 122 | (forward-char -1) 123 | (search-backward-regexp "[^\n ]" nil t) 124 | (line-end-position)) 125 | (when (search-forward-regexp "\n" nil t 1) 126 | (1- (point))) 127 | (point-max))))) 128 | (cons start end)))) 129 | 130 | (provide 'haskell-sort-imports) 131 | 132 | ;;; haskell-sort-imports.el ends here 133 | -------------------------------------------------------------------------------- /haskell-svg.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-svg.el --- SVG Rendering -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2018 Federico Beffa. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (defcustom haskell-svg-render-images nil 21 | "Replace SVG image text with actual images." 22 | :group 'haskell-interactive 23 | :type 'boolean) 24 | 25 | (defconst haskell-svg-supported (image-type-available-p 'svg) 26 | "Defines if SVG images are supported by this instance of Emacs.") 27 | 28 | 29 | 30 | (defun haskell-svg-render-images-p () 31 | "Shall we render SVG images?" 32 | (and haskell-svg-supported (display-images-p) haskell-svg-render-images)) 33 | 34 | (defun haskell-svg-maybe-render-images (text) 35 | "Render SVG images if desired and supported, or terurn the 36 | input unmodified." 37 | (if (haskell-svg-render-images-p) 38 | (haskell-svg-render-images text) 39 | text)) 40 | 41 | (defun haskell-svg-render-images (text) 42 | "Replace an SVG image text with an actual image." 43 | (with-temp-buffer 44 | (insert text) 45 | (goto-char (point-min)) 46 | (when (re-search-forward 47 | "\"?<\\?xml\\(.\\|\n\\|\r\\)* PUBLIC \"-//W3C//DTD SVG [0-9]\.[0-9]//EN\\(.\\|\n\\|\r\\)*</svg>\"?" 48 | nil t) 49 | (let ((svg-string (match-string 0)) 50 | (begin (match-beginning 0)) 51 | (end (match-end 0))) 52 | (delete-region begin end) 53 | (goto-char begin) 54 | (insert-image (create-image svg-string nil t) "SVG image"))) 55 | (buffer-substring (point-min) (point-max)))) 56 | 57 | (defun haskell-svg-toggle-render-images () 58 | "Toggle rendering of SVG images at the REPL output." 59 | (interactive) 60 | (setq haskell-svg-render-images (not haskell-svg-render-images))) 61 | 62 | 63 | 64 | (provide 'haskell-svg) 65 | 66 | ;;; haskell-svg.el ends here 67 | -------------------------------------------------------------------------------- /haskell-unicode-input-method.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-unicode-input-method.el --- Haskell Unicode helper functions -*- coding: utf-8; lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2010-2011 Roel van Dijk 4 | 5 | ;; Author: Roel van Dijk <vandijk.roel@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 21 | 22 | ;;; Commentary: 23 | 24 | ;;; Code: 25 | 26 | (require 'quail) 27 | 28 | ;;;###autoload 29 | (defun haskell-unicode-input-method-enable () 30 | "Set input method `haskell-unicode'." 31 | (interactive) 32 | (set-input-method "haskell-unicode")) 33 | 34 | ;;;###autoload 35 | (define-obsolete-function-alias 'turn-on-haskell-unicode-input-method 'haskell-unicode-input-method-enable "2020-04") 36 | 37 | (quail-define-package 38 | "haskell-unicode" ;; name 39 | "UTF-8" ;; language 40 | "\\" ;; title 41 | t ;; guidance 42 | "Haskell Unicode input method. 43 | Designed to be used with the Haskell UnicodeSyntax language 44 | extension in combination with the x-unicode-symbols set of 45 | packages (base-unicode-symbols and containers-unicode-symbols). 46 | " ;; docstring 47 | nil ;; translation-keys 48 | nil ;; forget-last-selection 49 | nil ;; deterministic 50 | nil ;; kbd-translate 51 | nil ;; show-layout 52 | nil ;; create-decode-map 53 | nil ;; maximum-shortest 54 | nil ;; overlay-plist 55 | nil ;; update-translation-function 56 | nil ;; conversion-keys 57 | t ;; simple 58 | ) 59 | 60 | (quail-define-rules 61 | ;; Greek letters 62 | ("alpha " ["α"]) 63 | ("Alpha " ["Α"]) 64 | ("beta " ["β"]) 65 | ("Beta " ["Β"]) 66 | ("gamma " ["γ"]) 67 | ("Gamma " ["Γ"]) 68 | ("delta " ["δ"]) 69 | ("Delta " ["Δ"]) 70 | ("epsilon " ["ε"]) 71 | ("Epsilon " ["Ε"]) 72 | ("zeta " ["ζ"]) 73 | ("Zeta " ["Ζ"]) 74 | ("eta " ["η"]) 75 | ("Eta " ["Η"]) 76 | ("theta " ["θ"]) 77 | ("Theta " ["Θ"]) 78 | ("iota " ["ι"]) 79 | ("Iota " ["Ι"]) 80 | ("kappa " ["κ"]) 81 | ("Kappa " ["Κ"]) 82 | ("lambda " ["λ"]) 83 | ("Lambda " ["Λ"]) 84 | ("lamda " ["λ"]) 85 | ("Lamda " ["Λ"]) 86 | ("mu " ["μ"]) 87 | ("Mu " ["Μ"]) 88 | ("nu " ["ν"]) 89 | ("Nu " ["Ν"]) 90 | ("xi " ["ξ"]) 91 | ("Xi " ["Ξ"]) 92 | ("omicron " ["ο"]) 93 | ("Omicron " ["Ο"]) 94 | ("pi " ["π"]) 95 | ("Pi " ["Π"]) 96 | ("rho " ["ρ"]) 97 | ("Rho " ["Ρ"]) 98 | ("sigma " ["σ"]) 99 | ("Sigma " ["Σ"]) 100 | ("tau " ["τ"]) 101 | ("Tau " ["Τ"]) 102 | ("upsilon " ["υ"]) 103 | ("Upsilon " ["Υ"]) 104 | ("phi " ["φ"]) 105 | ("Phi " ["Φ"]) 106 | ("chi " ["χ"]) 107 | ("Chi " ["Χ"]) 108 | ("psi " ["ψ"]) 109 | ("Psi " ["Ψ"]) 110 | ("omega " ["ω"]) 111 | ("Omega " ["Ω"]) 112 | ("digamma " ["ϝ"]) 113 | ("Digamma " ["Ϝ"]) 114 | ("san " ["ϻ"]) 115 | ("San " ["Ϻ"]) 116 | ("qoppa " ["ϙ"]) 117 | ("Qoppa " ["Ϙ"]) 118 | ("sampi " ["ϡ"]) 119 | ("Sampi " ["Ϡ"]) 120 | ("stigma " ["ϛ"]) 121 | ("Stigma " ["Ϛ"]) 122 | ("heta " ["ͱ"]) 123 | ("Heta " ["Ͱ"]) 124 | ("sho " ["ϸ"]) 125 | ("Sho " ["Ϸ"]) 126 | 127 | ;; Double-struck letters 128 | ("|A|" ["𝔸"]) 129 | ("|B|" ["𝔹"]) 130 | ("|C|" ["ℂ"]) 131 | ("|D|" ["𝔻"]) 132 | ("|E|" ["𝔼"]) 133 | ("|F|" ["𝔽"]) 134 | ("|G|" ["𝔾"]) 135 | ("|H|" ["ℍ"]) 136 | ("|I|" ["𝕀"]) 137 | ("|J|" ["𝕁"]) 138 | ("|K|" ["𝕂"]) 139 | ("|L|" ["𝕃"]) 140 | ("|M|" ["𝕄"]) 141 | ("|N|" ["ℕ"]) 142 | ("|O|" ["𝕆"]) 143 | ("|P|" ["ℙ"]) 144 | ("|Q|" ["ℚ"]) 145 | ("|R|" ["ℝ"]) 146 | ("|S|" ["𝕊"]) 147 | ("|T|" ["𝕋"]) 148 | ("|U|" ["𝕌"]) 149 | ("|V|" ["𝕍"]) 150 | ("|W|" ["𝕎"]) 151 | ("|X|" ["𝕏"]) 152 | ("|Y|" ["𝕐"]) 153 | ("|Z|" ["ℤ"]) 154 | ("|gamma|" ["ℽ"]) 155 | ("|Gamma|" ["ℾ"]) 156 | ("|pi|" ["ℼ"]) 157 | ("|Pi|" ["ℿ"]) 158 | 159 | ;; Types 160 | ("::" ["∷"]) 161 | 162 | ;; Quantifiers 163 | ("forall" ["∀"]) 164 | ("exists" ["∃"]) 165 | 166 | ;; Arrows 167 | ("->" ["→"]) 168 | ;; ("-->" ["⟶"]) 169 | ("<-" ["←"]) 170 | ;; ("<--" ["⟵"]) 171 | ;; ("<->" ["↔"]) 172 | ;; ("<-->" ["⟷"]) 173 | 174 | ("=>" ["⇒"]) 175 | ;; ("==>" ["⟹"]) 176 | ;; ("<=" ["⇐"]) 177 | ;; ("<==" ["⟸"]) 178 | ;; ("<=>" ["⇔"]) 179 | ;; ("<==>" ["⟺"]) 180 | 181 | ;; ("|->" ["↦"]) 182 | ;; ("|-->" ["⟼"]) 183 | ;; ("<-|" ["↤"]) 184 | ;; ("<--|" ["⟻"]) 185 | 186 | ;; ("|=>" ["⤇"]) 187 | ;; ("|==>" ["⟾"]) 188 | ;; ("<=|" ["⤆"]) 189 | ;; ("<==|" ["⟽"]) 190 | 191 | ("~>" ["⇝"]) 192 | ;; ("~~>" ["⟿"]) 193 | ("<~" ["⇜"]) 194 | ;; ("<~~" ["⬳"]) 195 | 196 | ;; (">->" ["↣"]) 197 | ;; ("<-<" ["↢"]) 198 | ;; ("->>" ["↠"]) 199 | ;; ("<<-" ["↞"]) 200 | 201 | ;; (">->>" ["⤖"]) 202 | ;; ("<<-<" ["⬻"]) 203 | 204 | ;; ("<|-" ["⇽"]) 205 | ;; ("-|>" ["⇾"]) 206 | ;; ("<|-|>" ["⇿"]) 207 | 208 | ;; ("<-/-" ["↚"]) 209 | ;; ("-/->" ["↛"]) 210 | 211 | ;; ("<-|-" ["⇷"]) 212 | ;; ("-|->" ["⇸"]) 213 | ;; ("<-|->" ["⇹"]) 214 | 215 | ;; ("<-||-" ["⇺"]) 216 | ;; ("-||->" ["⇻"]) 217 | ;; ("<-||->" ["⇼"]) 218 | 219 | ;; ("-o->" ["⇴"]) 220 | ;; ("<-o-" ["⬰"]) 221 | 222 | ;; Boolean operators 223 | ;; ("not" ["¬"]) 224 | ("&&" ["∧"]) 225 | ("||" ["∨"]) 226 | 227 | ;; Relational operators 228 | ("==" ["≡"]) 229 | ("/=" ["≢" "≠"]) 230 | ("<=" ["≤"]) 231 | (">=" ["≥"]) 232 | ("/<" ["≮"]) 233 | ("/>" ["≯"]) 234 | 235 | ;; Arithmetic 236 | ;; (" / " [" ÷ "]) 237 | (" * " [" ⋅ "]) 238 | 239 | ;; Containers / Collections 240 | ;; ("++" ["⧺"]) 241 | ;; ("+++" ["⧻"]) 242 | ;; ("|||" ["⫴"]) 243 | ;; ("empty" ["∅"]) 244 | ("elem" ["∈"]) 245 | ("notElem" ["∉"]) 246 | ("member" ["∈"]) 247 | ("notMember" ["∉"]) 248 | ("union" ["∪"]) 249 | ("intersection" ["∩"]) 250 | ("isSubsetOf" ["⊆"]) 251 | ("isProperSubsetOf" ["⊂"]) 252 | 253 | ;; Other 254 | ;; ("<<" ["≪"]) 255 | ;; (">>" ["≫"]) 256 | ("<<<" ["⋘"]) 257 | (">>>" ["⋙"]) 258 | ("<|" ["⊲"]) 259 | ("|>" ["⊳"]) 260 | ("><" ["⋈"]) 261 | ;; ("mempty" ["∅"]) 262 | ("mappend" ["⊕"]) 263 | ;; ("<*>" ["⊛"]) 264 | (" . " [" ∘ "]) 265 | ("undefined" ["⊥"]) 266 | (":=" ["≔"]) 267 | ("=:" ["≕"]) 268 | ("=def" ["≝"]) 269 | ("=?" ["≟"]) 270 | ("..." ["…"]) 271 | 272 | ;; Braces 273 | ;; ("[|" ["〚"]) 274 | ;; ("|]" ["〛"]) 275 | 276 | ;; Numeric subscripts 277 | ("_0 " ["₀"]) 278 | ("_1 " ["₁"]) 279 | ("_2 " ["₂"]) 280 | ("_3 " ["₃"]) 281 | ("_4 " ["₄"]) 282 | ("_5 " ["₅"]) 283 | ("_6 " ["₆"]) 284 | ("_7 " ["₇"]) 285 | ("_8 " ["₈"]) 286 | ("_9 " ["₉"]) 287 | 288 | ;; Numeric superscripts 289 | ("^0 " ["⁰"]) 290 | ("^1 " ["¹"]) 291 | ("^2 " ["²"]) 292 | ("^3 " ["³"]) 293 | ("^4 " ["⁴"]) 294 | ("^5 " ["⁵"]) 295 | ("^6 " ["⁶"]) 296 | ("^7 " ["⁷"]) 297 | ("^8 " ["⁸"]) 298 | ("^9 " ["⁹"])) 299 | 300 | (provide 'haskell-unicode-input-method) 301 | 302 | ;;; haskell-unicode-input-method.el ends here 303 | -------------------------------------------------------------------------------- /haskell-utils.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-utils.el --- General utility functions used by haskell-mode modules -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2013 Herbert Valerio Riedel 4 | ;; 2016 Arthur Fayzrakhmanov 5 | 6 | ;; Author: Herbert Valerio Riedel <hvr@gnu.org> 7 | 8 | ;; This file is not part of GNU Emacs. 9 | 10 | ;; This file is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This file is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 22 | 23 | ;;; Commentary: 24 | 25 | ;; This module's purpose is to provide a place for helper functions 26 | ;; which are general enough to be usable by multiple modules and/or 27 | ;; to alleviate circular module dependency problems. 28 | ;; 29 | ;; When possible, functions in this module shall be accompanied by 30 | ;; ERT-based unit tests. 31 | ;; 32 | ;; See also `haskell-str.el' for string utility functions. 33 | ;; 34 | ;; All symbols in this module have a `haskell-utils-' prefix. 35 | 36 | ;;; Code: 37 | 38 | ;; ============================================================================= 39 | ;; NOTE: 40 | ;; THIS MODULE IS SUPPOSED TO BE A LEAF-MODULE AND SHALL NOT REQUIRE/DEPEND-ON 41 | ;; ANY OTHER HASKELL-MODE MODULES IN ORDER TO STAY AT THE BOTTOM OF THE MODULE 42 | ;; DEPENDENCY GRAPH. 43 | ;; ============================================================================= 44 | 45 | (eval-when-compile (require 'cl-lib)) 46 | 47 | (defvar-local haskell-utils-async-post-command-flag nil 48 | "Non-nil means some commands were triggered during async function execution.") 49 | 50 | (defvar haskell-mode-interactive-prompt-state nil 51 | "Special variable indicating a state of user input waiting.") 52 | 53 | (defun haskell-utils-read-directory-name (prompt default) 54 | "Read directory name and normalize to true absolute path. 55 | Refer to `read-directory-name' for the meaning of PROMPT and 56 | DEFAULT. If `haskell-process-load-or-reload-prompt' is nil, 57 | accept `default'." 58 | (let ((filename (file-truename (read-directory-name prompt default default)))) 59 | (concat (replace-regexp-in-string "/$" "" filename) "/"))) 60 | 61 | (defun haskell-utils-parse-import-statement-at-point () 62 | "Return imported module name if on import statement or nil otherwise. 63 | This currently assumes that the \"import\" keyword and the module 64 | name are on the same line. 65 | 66 | This function supports the SafeHaskell and PackageImports syntax extensions. 67 | 68 | Note: doesn't detect if in {--}-style comment." 69 | (save-excursion 70 | (goto-char (line-beginning-position)) 71 | (if (looking-at (concat "[\t ]*import[\t ]+" 72 | "\\(?:safe[\t ]+\\)?" ;; SafeHaskell 73 | "\\(?:qualified[\t ]+\\)?" 74 | "\\(?:\"[^\"]*\"[\t ]+\\)?" ;; PackageImports 75 | "\\([[:digit:][:upper:][:lower:]_.]+\\)")) 76 | (match-string-no-properties 1)))) 77 | 78 | (defun haskell-utils-async-update-post-command-flag () 79 | "A special hook which collects triggered commands during async execution. 80 | This hook pushes value of variable `this-command' to flag variable 81 | `haskell-utils-async-post-command-flag'." 82 | (let* ((cmd this-command) 83 | (updated-flag (cons cmd haskell-utils-async-post-command-flag))) 84 | (setq haskell-utils-async-post-command-flag updated-flag))) 85 | 86 | (defun haskell-utils-async-watch-changes () 87 | "Watch for triggered commands during async operation execution. 88 | Resets flag variable 89 | `haskell-utils-async-update-post-command-flag' to NIL. By changes it is 90 | assumed that nothing happened, e.g. nothing was inserted in 91 | buffer, point was not moved, etc. To collect data `post-command-hook' is used." 92 | (setq haskell-utils-async-post-command-flag nil) 93 | (add-hook 94 | 'post-command-hook #'haskell-utils-async-update-post-command-flag nil t)) 95 | 96 | (defun haskell-utils-async-stop-watching-changes (buffer) 97 | "Clean up after async operation finished. 98 | This function takes care about cleaning up things made by 99 | `haskell-utils-async-watch-changes'. The BUFFER argument is a buffer where 100 | `post-command-hook' should be disabled. This is necessary, because 101 | it is possible that user will change buffer during async function 102 | execusion." 103 | (with-current-buffer buffer 104 | (setq haskell-utils-async-post-command-flag nil) 105 | (remove-hook 106 | 'post-command-hook #'haskell-utils-async-update-post-command-flag t))) 107 | 108 | (defun haskell-utils-reduce-string (str) 109 | "Remove newlines and extra whitespace from string STR. 110 | If line starts with a sequence of whitespaces, substitutes this 111 | sequence with a single whitespace. Removes all newline 112 | characters." 113 | (let ((s (replace-regexp-in-string "^\s+" " " str))) 114 | (replace-regexp-in-string "\r?\n" "" s))) 115 | 116 | (defun haskell-utils-repl-response-error-status (response) 117 | "Parse response REPL\\='s RESPONSE for errors. 118 | Returns one of the following symbols: 119 | 120 | + unknown-command 121 | + option-missing 122 | + interactive-error 123 | + no-error 124 | 125 | *Warning*: this function covers only three kind of responses: 126 | 127 | * \"unknown command …\" 128 | REPL missing requested command 129 | * \"<interactive>:3:5: …\" 130 | interactive REPL error 131 | * \"Couldn\\='t guess that module name. Does it exist?\" 132 | (:type-at and maybe some other commands error) 133 | * *all other reposnses* are treated as success reposneses and 134 | \\='no-error is returned." 135 | (if response 136 | (let ((first-line (car (split-string response "\n" t)))) 137 | (cond 138 | ((null first-line) 'no-error) 139 | ((string-match-p "^unknown command" first-line) 140 | 'unknown-command) 141 | ((string-match-p 142 | "^Couldn't guess that module name. Does it exist?" 143 | first-line) 144 | 'option-missing) 145 | ((string-match-p "^<interactive>:" first-line) 146 | 'interactive-error) 147 | (t 'no-error))) 148 | ;; in case of nil-ish response it's not clear is it error response or not 149 | 'no-error)) 150 | 151 | (defun haskell-utils-compose-type-at-command (pos) 152 | "Prepare :type-at command to be send to haskell process. 153 | POS is a cons cell containing min and max positions, i.e. target 154 | expression bounds." 155 | (save-excursion 156 | (let ((start-p (car pos)) 157 | (end-p (cdr pos)) 158 | start-l 159 | start-c 160 | end-l 161 | end-c 162 | value) 163 | (goto-char start-p) 164 | (setq start-l (line-number-at-pos)) 165 | (setq start-c (1+ (current-column))) 166 | (goto-char end-p) 167 | (setq end-l (line-number-at-pos)) 168 | (setq end-c (1+ (current-column))) 169 | (setq value (buffer-substring-no-properties start-p end-p)) 170 | ;; suppress multiline expressions 171 | (let ((lines (split-string value "\n" t))) 172 | (when (and (cdr lines) 173 | (stringp (car lines))) 174 | (setq value (format "[ %s … ]" (car lines))))) 175 | (replace-regexp-in-string 176 | "\n$" 177 | "" 178 | (format ":type-at %s %d %d %d %d %s" 179 | (buffer-file-name) 180 | start-l 181 | start-c 182 | end-l 183 | end-c 184 | value))))) 185 | 186 | 187 | (defun haskell-mode-toggle-interactive-prompt-state (&optional disabled) 188 | "Set `haskell-mode-interactive-prompt-state' to t. 189 | If given DISABLED argument sets variable value to nil, otherwise to t." 190 | (setq haskell-mode-interactive-prompt-state (not disabled))) 191 | 192 | (provide 'haskell-utils) 193 | ;;; haskell-utils.el ends here 194 | -------------------------------------------------------------------------------- /highlight-uses-mode.el: -------------------------------------------------------------------------------- 1 | ;;; highlight-uses-mode.el --- Mode for highlighting uses -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'cl-lib) 21 | 22 | (defvar highlight-uses-mode-map 23 | (let ((map (make-sparse-keymap))) 24 | (define-key map (kbd "TAB") 'highlight-uses-mode-next) 25 | (define-key map (kbd "S-TAB") 'highlight-uses-mode-prev) 26 | (define-key map (kbd "<backtab>") 'highlight-uses-mode-prev) 27 | (define-key map (kbd "RET") 'highlight-uses-mode-stop-here) 28 | (define-key map (kbd "C-g") 'highlight-uses-mode) 29 | map) 30 | "Keymap for using `highlight-uses-mode'.") 31 | 32 | (defvar-local highlight-uses-mode-point nil) 33 | 34 | ;;;###autoload 35 | (define-minor-mode highlight-uses-mode 36 | "Minor mode for highlighting and jumping between uses." 37 | :lighter " Uses" 38 | :keymap highlight-uses-mode-map 39 | (if highlight-uses-mode 40 | (setq highlight-uses-mode-point (point)) 41 | (when highlight-uses-mode-point 42 | (goto-char highlight-uses-mode-point))) 43 | (remove-overlays (point-min) (point-max) 'highlight-uses-mode-highlight t)) 44 | 45 | (defun highlight-uses-mode-replace () 46 | "Replace all highlighted instances in the buffer with something 47 | else." 48 | (interactive) 49 | (save-excursion 50 | (goto-char (point-min)) 51 | (let ((o (highlight-uses-mode-next))) 52 | (when o 53 | (let ((replacement (read-from-minibuffer (format "Replace uses %s with: " 54 | (buffer-substring 55 | (overlay-start o) 56 | (overlay-end o)))))) 57 | 58 | (while o 59 | (goto-char (overlay-start o)) 60 | (delete-region (overlay-start o) 61 | (overlay-end o)) 62 | (insert replacement) 63 | (setq o (highlight-uses-mode-next)))))))) 64 | 65 | (defun highlight-uses-mode-stop-here () 66 | "Stop at this point." 67 | (interactive) 68 | (setq highlight-uses-mode-point (point)) 69 | (highlight-uses-mode -1)) 70 | 71 | (defun highlight-uses-mode-next () 72 | "Jump to next result." 73 | (interactive) 74 | (let ((os (sort (cl-remove-if (lambda (o) 75 | (or (<= (overlay-start o) (point)) 76 | (not (overlay-get o 'highlight-uses-mode-highlight)))) 77 | (overlays-in (point) (point-max))) 78 | (lambda (a b) 79 | (< (overlay-start a) 80 | (overlay-start b)))))) 81 | (when os 82 | (goto-char (overlay-start (car os))) 83 | (car os)))) 84 | 85 | (defun highlight-uses-mode-prev () 86 | "Jump to previous result." 87 | (interactive) 88 | (let ((os (sort (cl-remove-if (lambda (o) 89 | (or (>= (overlay-end o) (point)) 90 | (not (overlay-get o 'highlight-uses-mode-highlight)))) 91 | (overlays-in (point-min) (point))) 92 | (lambda (a b) 93 | (> (overlay-start a) 94 | (overlay-start b)))))) 95 | (when os 96 | (goto-char (overlay-start (car os))) 97 | (car os)))) 98 | 99 | (defun highlight-uses-mode-highlight (start end) 100 | "Make a highlight overlay at the given span." 101 | (let ((o (make-overlay start end))) 102 | (overlay-put o 'priority 999) 103 | (overlay-put o 'face 'isearch) 104 | (overlay-put o 'highlight-uses-mode-highlight t))) 105 | 106 | (provide 'highlight-uses-mode) 107 | -------------------------------------------------------------------------------- /images/haskell-mode-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/images/haskell-mode-128x128.png -------------------------------------------------------------------------------- /images/haskell-mode-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/images/haskell-mode-256x256.png -------------------------------------------------------------------------------- /images/haskell-mode-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/images/haskell-mode-32x32.png -------------------------------------------------------------------------------- /images/haskell-mode-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/images/haskell-mode-512x512.png -------------------------------------------------------------------------------- /images/haskell-mode-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/images/haskell-mode-64x64.png -------------------------------------------------------------------------------- /images/star-history-700.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell/haskell-mode/2e08ba771ffdc46d082b2285c534afdb12efb941/images/star-history-700.png -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="481.8897pt" height="340.1574pt" viewBox="0 0 481.8897 340.1574" version="1.1"> 3 | <defs> 4 | <clipPath id="clip1"> 5 | <path d="M 0 340.15625 L 481.890625 340.15625 L 481.890625 0 L 0 0 L 0 340.15625 Z M 0 340.15625 "/> 6 | </clipPath> 7 | </defs> 8 | <g id="surface0"> 9 | <g clip-path="url(#clip1)" clip-rule="nonzero"> 10 | <path style=" stroke:none;fill-rule: nonzero; fill: rgb(40%,40%,40%); fill-opacity: 1;" d="M 0 340.15625 L 113.386719 170.078125 L 0 0 L 85.039062 0 L 198.425781 170.078125 L 85.039062 340.15625 L 0 340.15625 Z M 0 340.15625 "/> 11 | <path style=" stroke:none;fill-rule: nonzero; fill: rgb(60%,60%,60%); fill-opacity: 1;" d="M 113.386719 340.15625 L 226.773438 170.078125 L 113.386719 0 L 198.425781 0 L 425.195312 340.15625 L 340.15625 340.15625 L 269.292969 233.859375 L 198.425781 340.15625 L 113.386719 340.15625 Z M 113.386719 340.15625 "/> 12 | <path style=" stroke:none;fill-rule: nonzero; fill: rgb(40%,40%,40%); fill-opacity: 1;" d="M 387.402344 240.945312 L 349.609375 184.253906 L 481.890625 184.25 L 481.890625 240.945312 L 387.402344 240.945312 Z M 387.402344 240.945312 "/> 13 | <path style=" stroke:none;fill-rule: nonzero; fill: rgb(40%,40%,40%); fill-opacity: 1;" d="M 330.710938 155.90625 L 292.914062 99.214844 L 481.890625 99.210938 L 481.890625 155.90625 L 330.710938 155.90625 Z M 330.710938 155.90625 "/> 14 | </g> 15 | </g> 16 | </svg> 17 | -------------------------------------------------------------------------------- /tests/haskell-collapse-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-collapse-tests.el --- tests for collapse module -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2017 Vasantha Ganesh K. <vasanthaganesh.k@tuta.io> 4 | 5 | ;; This file is not part of GNU Emacs. 6 | 7 | ;; This file is free software; you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation; either version 3, or (at your option) 10 | ;; any later version. 11 | 12 | ;; This file is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 19 | 20 | (require 'ert) 21 | (require 'haskell-collapse) 22 | 23 | (setq haskell-code-block-1 "instance FromJSON BlogConfig where 24 | parseJSON (Object v) = BlogConfig <$> 25 | v .: \"blogname\" <*> 26 | v .: \"tagline\" <*> 27 | v .: \"author\" <*> 28 | v .: \"email\" <*> 29 | v .: \"twitter\" <*> 30 | v .: \"gitlab\" <*> 31 | v .: \"github\" 32 | 33 | -- A non-Object value is of the wrong type, so fail. 34 | parseJSON _ = error \"Can't parse BlogConfig from YAML\" 35 | ") 36 | 37 | (setq haskell-code-block-2 "-------------------------------------------------------------------------------- 38 | {-# LANGUAGE OverloadedStrings #-} 39 | import Data.Monoid 40 | import Hakyll.Web.Sass (sassCompiler) 41 | import Hakyll 42 | import Control.Applicative 43 | import Data.Yaml 44 | import Data.Maybe 45 | import qualified Data.ByteString.Char8 as BS 46 | 47 | -------------------------------------------------------------------------------- 48 | ") 49 | 50 | (setq haskell-code-block-3 "archiveCtx posts blogconfig = listField \"posts\" (postCtx blogconfig) (return posts) <> 51 | constField \"title\" \"Archive\" <> 52 | defaultCTX blogconfig 53 | 54 | 55 | 56 | indexCtx posts blogconfig = listField \"posts\" (postCtx blogconfig) (return (take 5 posts)) <> 57 | constField \"title\" \"Posts\" <> 58 | defaultCTX blogconfig 59 | ") 60 | 61 | (defun test-haskell-collapse-start-end (start end) 62 | (let ((start (save-excursion 63 | (beginning-of-buffer) 64 | (forward-line start) 65 | (end-of-line) 66 | (point))) 67 | (end (save-excursion 68 | (beginning-of-buffer) 69 | (forward-line end) 70 | (end-of-line) 71 | (point)))) 72 | (cons start end))) 73 | 74 | (defun test-haskell-indented-block (source lines result) 75 | "takes args source (source-code)" 76 | (with-temp-buffer 77 | (insert source) 78 | (beginning-of-buffer) 79 | (forward-line lines) 80 | (equal (funcall result) 81 | (haskell-indented-block)))) 82 | 83 | (ert-deftest test-haskell-indented-block-1 () 84 | (should (test-haskell-indented-block 85 | haskell-code-block-1 86 | 1 87 | (lambda () (test-haskell-collapse-start-end 1 9))))) 88 | 89 | (ert-deftest test-haskell-indented-block-2 () 90 | (should (test-haskell-indented-block 91 | haskell-code-block-1 92 | 0 93 | (lambda () (test-haskell-collapse-start-end 0 11))))) 94 | 95 | 96 | (ert-deftest test-haskell-indented-block-3 () 97 | (should (test-haskell-indented-block 98 | haskell-code-block-1 99 | 2 100 | (lambda () (test-haskell-collapse-start-end 1 9))))) 101 | 102 | (ert-deftest test-haskell-indented-block-4 () 103 | (should (test-haskell-indented-block 104 | haskell-code-block-2 105 | 0 106 | (lambda () nil)))) 107 | 108 | (ert-deftest test-haskell-indented-block-5 () 109 | (should (test-haskell-indented-block 110 | haskell-code-block-2 111 | 1 112 | (lambda () nil)))) 113 | 114 | (ert-deftest test-haskell-indented-block-6 () 115 | (should (test-haskell-indented-block 116 | haskell-code-block-2 117 | 3 118 | (lambda () nil)))) 119 | 120 | (ert-deftest test-haskell-indented-block-7 () 121 | (should (test-haskell-indented-block 122 | haskell-code-block-3 123 | 0 124 | (lambda () (test-haskell-collapse-start-end 0 5))))) 125 | -------------------------------------------------------------------------------- /tests/haskell-customize-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-customize.el --- Customization settings -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Vasantha Ganesh Kanniappan <vasanthaganesh.k@tuta.io> 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'ert) 21 | (require 'haskell-customize) 22 | (require 'haskell-test-utils) 23 | 24 | (defvar dir-structure nil) 25 | 26 | (ert-deftest haskell-process-type-test-1 () 27 | (with-temp-dir-structure 28 | (("README.md" . "Hello world") 29 | ("Main.hs" . "-- Empty file") 30 | ("abc.cabal" . "-- Empty File") 31 | ("stack.yaml" . "# Empty file") 32 | ("src" . (("moduleA.hs" . "-- Empty file") 33 | ("moduleB.hs" . "-- Empty file"))) 34 | ("tests" . (("test1.hs" . "-- Empty file") 35 | ("test2.hs" . "-- Empty file")))) 36 | (progn 37 | (cd "tests") 38 | (should (eq 'stack-ghci (haskell-process-type)))))) 39 | 40 | (ert-deftest haskell-process-type-test-2 () 41 | (with-temp-dir-structure 42 | (("README.md" . "Hello world") 43 | ("Main.hs" . "-- Empty file") 44 | ("stack.yaml" . "# Empty file") 45 | ("src" . (("moduleA.hs" . "-- Empty file") 46 | ("moduleB.hs" . "-- Empty file"))) 47 | ("tests" . (("test1.hs" . "-- Empty file") 48 | ("test2.hs" . "-- Empty file")))) 49 | (progn 50 | (cd "src") 51 | (should (eq 'stack-ghci (haskell-process-type)))))) 52 | 53 | (ert-deftest haskell-process-type-test-3 () 54 | (with-temp-dir-structure 55 | (("README.md" . "Hello world") 56 | ("Main.hs" . "-- Empty file") 57 | ("abc.cabal" . "-- Empty file") 58 | ("src" . (("moduleA.hs" . "-- Empty file") 59 | ("moduleB.hs" . "-- Empty file"))) 60 | ("tests" . (("test1.hs" . "-- Empty file") 61 | ("test2.hs" . "-- Empty file")))) 62 | (progn 63 | (should (eq 'cabal-repl (haskell-process-type)))))) 64 | 65 | (ert-deftest haskell-process-type-test-4 () 66 | (with-temp-dir-structure 67 | (("README.md" . "Hello world") 68 | ("Main.hs" . "-- Empty file") 69 | ("src" . (("moduleA.hs" . "-- Empty file") 70 | ("moduleB.hs" . "-- Empty file"))) 71 | ("tests" . (("test1.hs" . "-- Empty file") 72 | ("test2.hs" . "-- Empty file")))) 73 | (progn 74 | (should (eq 'ghci (haskell-process-type)))))) 75 | -------------------------------------------------------------------------------- /tests/haskell-decl-scan-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-decl-scan-tests.el -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2016 Chris Gregory. All rights reserved. 4 | 5 | ;; This file is part of haskell-mode package. 6 | ;; You can contact with authors using GitHub issue tracker: 7 | ;; https://github.com/haskell/haskell-mode/issues 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This package provides regression tests for haskell-decl-scan package. 27 | 28 | ;;; Code: 29 | 30 | (require 'ert) 31 | (require 'haskell-decl-scan) 32 | (require 'haskell-test-utils) 33 | 34 | (ert-deftest haskell-ds-line-commented-p-1 () 35 | "All lines in this buffer should count as comments" 36 | (with-temp-buffer 37 | (haskell-mode) 38 | (insert-lines "" "--hi" " -- hi\t " "" "{-hi-}" " \t{-hi-} " 39 | "-- | hi" "{-| hi -}") 40 | (font-lock-fontify-buffer) 41 | (goto-char (point-min)) 42 | 43 | (while (not (eobp)) 44 | (should (haskell-ds-line-commented-p)) 45 | (forward-line)))) 46 | 47 | (ert-deftest haskell-ds-comment-p-1 () 48 | "All characters in this buffer should count as comments" 49 | (with-temp-buffer 50 | (haskell-mode) 51 | (insert-lines "" "--hi" " -- hi\t " "" "{-hi-}" " \t{-hi-} ") 52 | (font-lock-fontify-buffer) 53 | (goto-char (point-min)) 54 | 55 | (while (not (bobp)) 56 | (should (haskell-ds-comment-p)) 57 | (forward-char)))) 58 | 59 | (ert-deftest haskell-ds-backward-decl-1 () 60 | "Test running haskell-ds-backward-decl" 61 | (with-temp-buffer 62 | (insert-lines "" "fun :: Int -> Int" "fun = id" 63 | "" "f2 :: Int" "f2 = 3" "") 64 | (goto-char (point-max)) 65 | 66 | (should (haskell-ds-backward-decl)) 67 | (should (looking-at-p "f2 :: Int")) 68 | 69 | (should (haskell-ds-backward-decl)) 70 | (should (looking-at-p "fun :: Int -> Int")) 71 | 72 | (should-not (haskell-ds-backward-decl)) 73 | (should (bobp)))) 74 | 75 | (ert-deftest haskell-ds-backward-decl-2-commented () 76 | "Test running haskell-ds-backward-decl" 77 | (with-temp-buffer 78 | (haskell-mode) 79 | (insert-lines "" "-- documentation" "fun :: Int -> Int" 80 | "" "{- comment -}" "fun = id" 81 | "" " -- space comment" "f2 :: Int" 82 | "" " {- trailing -} \t" "f2 = 3" 83 | "" "" "") 84 | (font-lock-fontify-buffer) 85 | (goto-char (point-max)) 86 | 87 | (should (haskell-ds-backward-decl)) 88 | (should (looking-at-p "f2 :: Int")) 89 | 90 | (should (haskell-ds-backward-decl)) 91 | (should (looking-at-p "fun :: Int -> Int")) 92 | 93 | (should-not (haskell-ds-backward-decl)) 94 | (should (bobp)))) 95 | 96 | (ert-deftest haskell-ds-backward-decl-2 () 97 | "Test running haskell-ds-backward-decl" 98 | (with-temp-buffer 99 | (insert-lines "" "" "fun :: Int -> Int" 100 | "" "" "fun = id" 101 | "" "" "f2 :: Int" 102 | "" "" "f2 = 3" 103 | "" "" "") 104 | (goto-char (point-max)) 105 | 106 | (should (haskell-ds-backward-decl)) 107 | (should (looking-at-p "f2 :: Int")) 108 | 109 | (should (haskell-ds-backward-decl)) 110 | (should (looking-at-p "fun :: Int -> Int")) 111 | 112 | (should-not (haskell-ds-backward-decl)) 113 | (should (= (point-min) (point))))) 114 | 115 | (ert-deftest haskell-ds-forward-decl-1 () 116 | "Test running haskell-ds-forward-decl" 117 | (with-temp-buffer 118 | (insert-lines "" "fun :: Int -> Int" "fun = id" 119 | "" "f2 :: Int" "f2 = 3" 120 | "") 121 | (goto-char (point-min)) 122 | 123 | (should (haskell-ds-forward-decl)) 124 | (should (looking-at-p "$")) 125 | (should (= (point) (save-excursion (goto-line 4) (point)))) 126 | 127 | (should (haskell-ds-forward-decl)) 128 | (should (looking-at-p "f2 :: Int")) 129 | 130 | (should (= (point-max) (haskell-ds-forward-decl))) 131 | (should (eobp)))) 132 | 133 | (ert-deftest haskell-ds-forward-decl-2 () 134 | "Test running haskell-ds-forward-decl" 135 | (with-temp-buffer 136 | (insert-lines "" "" "fun :: Int -> Int" 137 | "" "" "fun = id" 138 | "" "" "f2 :: Int" 139 | "" "" "f2 = 3" 140 | "" "" "") 141 | (goto-char (point-min)) 142 | 143 | (should (haskell-ds-forward-decl)) 144 | (should (looking-at-p "$")) 145 | (should (= (point) (save-excursion (goto-line 7) (point)))) 146 | 147 | (should (haskell-ds-forward-decl)) 148 | (should (looking-at-p "f2 :: Int")) 149 | 150 | (should (haskell-ds-forward-decl)) 151 | (should (= (point) (save-excursion (goto-line 13) (point)))) 152 | 153 | (should (= (point-max) (progn (haskell-ds-forward-decl) (point)))) 154 | (should (eobp)))) 155 | 156 | (ert-deftest haskell-ds-forward-decl-2-commented () 157 | "Test running haskell-ds-backward-decl" 158 | (with-temp-buffer 159 | (haskell-mode) 160 | (insert-lines "" "-- documentation" "fun :: Int -> Int" 161 | "" "{- comment -}" "fun = id" 162 | "" " -- space comment" "f2 :: Int" 163 | "" " {- trailing -} \t" "f2 = 3" 164 | "" "" "") 165 | (font-lock-fontify-buffer) 166 | (goto-char (point-min)) 167 | 168 | (should (haskell-ds-forward-decl)) 169 | (should (looking-at-p "$")) 170 | (should (= (point) (save-excursion (goto-line 7) (point)))) 171 | 172 | (should (haskell-ds-forward-decl)) 173 | (should (looking-at-p "f2 :: Int")) 174 | 175 | (should (haskell-ds-forward-decl)) 176 | (should (= (point) (save-excursion (goto-line 13) (point)))) 177 | 178 | (should (= (point-max) (progn (haskell-ds-forward-decl) (point)))) 179 | (should (eobp)))) 180 | 181 | (provide 'haskell-decl-scan-tests) 182 | 183 | ;;; haskell-decl-scan-tests.el ends here 184 | -------------------------------------------------------------------------------- /tests/haskell-doc-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-doc-tests.el --- Tests for `haskell-docs' package -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2016 Arthur Fayzrakhmanov. All rights reserved. 4 | 5 | ;; This file is part of haskell-mode package. 6 | ;; You can contact with authors using GitHub issue tracker: 7 | ;; https://github.com/haskell/haskell-mode/issues 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This package provides regression tests for haskell-docs package. 27 | 28 | ;;; Code: 29 | 30 | (require 'ert) 31 | (require 'haskell-mode) 32 | (require 'haskell-doc) 33 | (require 'haskell-test-utils) 34 | (require 'haskell-utils) 35 | 36 | 37 | (ert-deftest interactive-prompt-state () 38 | (with-temp-buffer 39 | (haskell-mode) 40 | (haskell-doc-mode) 41 | (insert-lines "module A where" 42 | "import B") 43 | (goto-char (point-min)) 44 | (forward-line) 45 | (should (string= 46 | "import [qualified] modid [as modid] [impspec]" 47 | (haskell-doc-eldoc-function))) 48 | (haskell-mode-toggle-interactive-prompt-state) 49 | (should (eq nil 50 | (haskell-doc-eldoc-function))) 51 | (haskell-mode-toggle-interactive-prompt-state t) 52 | (should (string= 53 | "import [qualified] modid [as modid] [impspec]" 54 | (haskell-doc-eldoc-function))))) 55 | 56 | ;;; haskell-doc-tests.el ends here 57 | -------------------------------------------------------------------------------- /tests/haskell-exec-tests.el: -------------------------------------------------------------------------------- 1 | ;; haskell-exec-tests.el --- -*- lexical-binding: t; -*- 2 | 3 | (require 'ert) 4 | (require 'haskell-test-utils) 5 | 6 | (defvar haskell-example-script "echo") 7 | 8 | (defun haskell-exec-test-output-argv-and-copy-stdin () 9 | (let (line) 10 | (while argv 11 | (message-stdout "%s" (pop argv))) 12 | (while (setq line (read-stdin)) 13 | (message-stdout "%s" line)))) 14 | 15 | (ert-deftest haskell-exec-subst-script () 16 | (with-script-path haskell-example-script haskell-exec-test-output-argv-and-copy-stdin 17 | (with-temp-buffer 18 | (insert "line1\n") 19 | (insert "line2\n") 20 | (insert "line3-no-newline") 21 | (call-process-region (point-min) (point-max) haskell-example-script t t nil "-a" "--arg1" "/zonk" "filename.el") 22 | (should (equal "-a\n--arg1\n/zonk\nfilename.el\nline1\nline2\nline3-no-newline\n" 23 | (buffer-substring-no-properties (point-min) (point-max))))))) 24 | -------------------------------------------------------------------------------- /tests/haskell-load-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-load-tests.el -*- lexical-binding: t -*- 2 | 3 | ;;; Code: 4 | 5 | (require 'cl-lib) 6 | (require 'ert) 7 | (require 'haskell-test-utils) 8 | 9 | (require 'haskell-load) 10 | 11 | (defun insert-errors () 12 | (insert "import Control.Applicativ\nimport Data.Mayb\nimport Data.String") 13 | (goto-char 1) 14 | (let ((applicativ (progn 15 | (search-forward "Control.Applicativ") 16 | (make-overlay (match-beginning 0) (match-end 0))))) 17 | (overlay-put applicativ 'haskell-check t) 18 | (overlay-put applicativ 'haskell-msg-type 'error) 19 | (overlay-put applicativ 'haskell-msg "Could not find module ‘Control.Applicativ’\n Perhaps you meant Control.Applicative (from base-4.8.1.0)\n Use -v to see a list of the files searched for.")) 20 | (let ((mayb (progn 21 | (search-forward "Data.Mayb") 22 | (make-overlay (match-beginning 0) (match-end 0))))) 23 | (overlay-put mayb 'haskell-check t) 24 | (overlay-put mayb 'haskell-msg-type 'error) 25 | (overlay-put mayb 'haskell-msg "Could not find module ‘Data.Mayb’\n Perhaps you meant\n Data.Maybe (from base-4.8.1.0)\n Data.Map (from containers-0.5.6.2@conta_LKCPrTJwOTOLk4OU37YmeN)\n Use -v to see a list of the files searched for.")) 26 | (goto-char 1)) 27 | 28 | (ert-deftest goto-first-error-before () 29 | (with-temp-switch-to-buffer 30 | (insert-errors) 31 | (haskell-goto-first-error) 32 | (should (looking-at-p "Control.Applicativ")))) 33 | 34 | (ert-deftest goto-first-error-after () 35 | (with-temp-switch-to-buffer 36 | (insert-errors) 37 | (search-forward "Data.String") 38 | (haskell-goto-first-error) 39 | (should (looking-at-p "Control.Applicativ")))) 40 | 41 | (ert-deftest goto-first-error-between () 42 | (with-temp-switch-to-buffer 43 | (insert-errors) 44 | (search-forward "import Data.Mayb") 45 | (haskell-goto-first-error) 46 | (should (looking-at-p "Control.Applicativ")))) 47 | 48 | (ert-deftest goto-next-error-before () 49 | (with-temp-switch-to-buffer 50 | (insert-errors) 51 | (haskell-goto-next-error) 52 | (should (looking-at-p "Control.Applicativ")))) 53 | 54 | (ert-deftest goto-next-error-between () 55 | (with-temp-switch-to-buffer 56 | (insert-errors) 57 | (search-forward "import" nil nil 2) 58 | (haskell-goto-next-error) 59 | (should (looking-at-p "Data.Mayb")))) 60 | 61 | (ert-deftest goto-next-error-after () 62 | (with-temp-switch-to-buffer 63 | (insert-errors) 64 | (search-forward "import" nil nil 3) 65 | (haskell-goto-next-error) 66 | (should (looking-at-p " Data.String")))) 67 | 68 | (ert-deftest goto-prev-error-before () 69 | (with-temp-switch-to-buffer 70 | (insert-errors) 71 | (haskell-goto-prev-error) 72 | (should (looking-at-p "import Control.Applicativ")))) 73 | 74 | (ert-deftest goto-prev-error-between () 75 | (with-temp-switch-to-buffer 76 | (insert-errors) 77 | (search-forward "import" nil nil 2) 78 | (haskell-goto-prev-error) 79 | (should (looking-at-p "Control.Applicativ")))) 80 | 81 | (ert-deftest goto-prev-error-after () 82 | (with-temp-switch-to-buffer 83 | (insert-errors) 84 | (search-forward "import Data.String") 85 | (haskell-goto-prev-error) 86 | (should (looking-at-p "Data.Mayb")))) 87 | 88 | (ert-deftest do-cabal-no-process () 89 | "Ensure that haskell-process-do-cabal can call cabal directly. 90 | 91 | Redefine `shell-command' to just capture the command it's asked 92 | to execute, and make sure it matches what we expected." 93 | (let (shell-call) 94 | (cl-letf (((symbol-function 'shell-command) (lambda (command &optional input-buffer output-buffer) 95 | (setq shell-call command)))) 96 | (haskell-process-do-cabal "help") 97 | (should (equal shell-call "cabal help"))))) 98 | -------------------------------------------------------------------------------- /tests/haskell-process-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-process-tests.el -*- lexical-binding: t -*- 2 | 3 | ;;; Code: 4 | 5 | (require 'ert) 6 | (require 'cl-lib) 7 | (require 'haskell-process) 8 | 9 | (ert-deftest haskell-process-wrapper-command-function-identity () 10 | "No wrapper, return directly the command." 11 | (should (equal '("ghci") 12 | (progn 13 | (custom-set-variables '(haskell-process-wrapper-function #'identity)) 14 | (apply haskell-process-wrapper-function (list '("ghci"))))))) 15 | 16 | (ert-deftest haskell-process-wrapper-function-non-identity () 17 | "Wrapper as a string, return the wrapping command as a string." 18 | (should (equal '("nix-shell" "default.nix" "--command" "cabal\\ run") 19 | (progn 20 | (custom-set-variables '(haskell-process-wrapper-function (lambda (argv) 21 | (append '("nix-shell" "default.nix" "--command") 22 | (list (shell-quote-argument argv)))))) 23 | (apply haskell-process-wrapper-function (list "cabal run")))))) 24 | 25 | (ert-deftest test-haskell-process--compute-process-log-and-command-ghci () 26 | (should (equal '("Starting inferior GHCi process ghci ..." "dumses1" nil "ghci" "-ferror-spans") 27 | (let ((haskell-process-path-ghci "ghci") 28 | (haskell-process-args-ghci '("-ferror-spans"))) 29 | (custom-set-variables '(haskell-process-wrapper-function #'identity)) 30 | (cl-letf (((symbol-function 'haskell-session-name) (lambda (session) "dumses1"))) 31 | (haskell-process-compute-process-log-and-command "dummy-session" 'ghci)))))) 32 | 33 | (ert-deftest test-haskell-process--with-wrapper-compute-process-log-and-command-ghci () 34 | (should (equal '("Starting inferior GHCi process ghci ..." "dumses1" nil "nix-shell" "default.nix" "--command" "ghci\\ -ferror-spans") 35 | (let ((haskell-process-path-ghci "ghci") 36 | (haskell-process-args-ghci '("-ferror-spans"))) 37 | (custom-set-variables '(haskell-process-wrapper-function 38 | (lambda (argv) (append (list "nix-shell" "default.nix" "--command" ) 39 | (list (shell-quote-argument (mapconcat 'identity argv " "))))))) 40 | (cl-letf (((symbol-function 'haskell-session-name) (lambda (session) "dumses1"))) 41 | (haskell-process-compute-process-log-and-command "dummy-session" 'ghci)))))) 42 | 43 | (ert-deftest test-haskell-process--compute-process-log-and-command-cabal-repl () 44 | (should (equal '("Starting inferior `cabal repl' process using cabal ..." "dumses2" nil "cabal" "repl" "--ghc-option=-ferror-spans" "dumdum-session") 45 | (let ((haskell-process-path-cabal "cabal") 46 | (haskell-process-args-cabal-repl '("--ghc-option=-ferror-spans"))) 47 | (custom-set-variables '(haskell-process-wrapper-function #'identity)) 48 | (cl-letf (((symbol-function 'haskell-session-name) (lambda (session) "dumses2")) 49 | ((symbol-function 'haskell-session-target) (lambda (session) "dumdum-session"))) 50 | (haskell-process-compute-process-log-and-command "dummy-session2" 'cabal-repl)))))) 51 | 52 | (ert-deftest test-haskell-process--with-wrapper-compute-process-log-and-command-cabal-repl () 53 | (should (equal '("Starting inferior `cabal repl' process using cabal ..." "dumses2" nil "nix-shell" "default.nix" "--command" "cabal\\ repl\\ --ghc-option\\=-ferror-spans\\ dumdum-session") 54 | (let ((haskell-process-path-cabal "cabal") 55 | (haskell-process-args-cabal-repl '("--ghc-option=-ferror-spans"))) 56 | (custom-set-variables '(haskell-process-wrapper-function 57 | (lambda (argv) (append (list "nix-shell" "default.nix" "--command" ) 58 | (list (shell-quote-argument (mapconcat 'identity argv " "))))))) 59 | (cl-letf (((symbol-function 'haskell-session-name) (lambda (session) "dumses2")) 60 | ((symbol-function 'haskell-session-target) (lambda (session) "dumdum-session"))) 61 | (haskell-process-compute-process-log-and-command "dummy-session2" 'cabal-repl)))))) 62 | 63 | 64 | ;;; haskell-process-tests.el ends here 65 | -------------------------------------------------------------------------------- /tests/haskell-sort-imports-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-sort-imports-tests.el --- Unit tests for haskell-sort-imports -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (c) 2014 Chris Done. All rights reserved. 4 | 5 | ;; This file is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 3, or (at your option) 8 | ;; any later version. 9 | 10 | ;; This file is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 17 | 18 | ;;; Code: 19 | 20 | (require 'ert) 21 | (require 'haskell-sort-imports) 22 | 23 | (ert-deftest empty-buffer () 24 | (should (with-temp-buffer 25 | (haskell-sort-imports) 26 | t))) 27 | 28 | (ert-deftest single-line () 29 | (should (with-temp-buffer 30 | (insert "import A\n") 31 | (goto-char (point-min)) 32 | (haskell-sort-imports) 33 | (string= (buffer-string) 34 | "import A\n")))) 35 | 36 | (ert-deftest two-idem () 37 | (should (with-temp-buffer 38 | (insert "import A 39 | import B 40 | ") 41 | (goto-char (point-min)) 42 | (haskell-sort-imports) 43 | (string= (buffer-string) 44 | "import A 45 | import B 46 | "))) 47 | (should (with-temp-buffer 48 | (insert "import qualified A 49 | import B 50 | ") 51 | (goto-char (point-min)) 52 | (haskell-sort-imports) 53 | (string= (buffer-string) 54 | "import qualified A 55 | import B 56 | "))) 57 | (should (with-temp-buffer 58 | (insert "import qualified \"mtl\" A 59 | import B 60 | ") 61 | (goto-char (point-min)) 62 | (haskell-sort-imports) 63 | (string= (buffer-string) 64 | "import qualified \"mtl\" A 65 | import B 66 | ")))) 67 | 68 | (ert-deftest two-rev () 69 | (should (with-temp-buffer 70 | (insert "import B 71 | import A 72 | ") 73 | (goto-char (point-min)) 74 | (haskell-sort-imports) 75 | (string= (buffer-string) 76 | "import A 77 | import B 78 | ")))) 79 | 80 | (ert-deftest file-structure () 81 | (should (with-temp-buffer 82 | (insert "module A where 83 | import B 84 | import A 85 | ") 86 | (goto-char (point-min)) 87 | (forward-line) 88 | (haskell-sort-imports) 89 | (string= (buffer-string) 90 | "module A where 91 | import A 92 | import B 93 | "))) 94 | (should (with-temp-buffer 95 | (insert "module C where 96 | 97 | import B 98 | import A 99 | ") 100 | (goto-char (point-min)) 101 | (forward-line 2) 102 | (haskell-sort-imports) 103 | (string= (buffer-string) 104 | "module C where 105 | 106 | import A 107 | import B 108 | ")))) 109 | 110 | (ert-deftest bos-270 () 111 | (should (with-temp-buffer 112 | (insert "import Data.Aeson.Encode (encode) 113 | import Data.Aeson.Types 114 | import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, 115 | eitherDecodeWith, eitherDecodeStrictWith, 116 | jsonEOF, json, jsonEOF', json') 117 | import qualified Data.ByteString as B 118 | import qualified Data.ByteString.Lazy as L 119 | ") 120 | (goto-char (point-min)) 121 | (haskell-sort-imports) 122 | (string= (buffer-string) 123 | "import Data.Aeson.Encode (encode) 124 | import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, 125 | eitherDecodeWith, eitherDecodeStrictWith, 126 | jsonEOF, json, jsonEOF', json') 127 | import Data.Aeson.Types 128 | import qualified Data.ByteString as B 129 | import qualified Data.ByteString.Lazy as L 130 | ")))) 131 | 132 | (provide 'haskell-sort-imports-tests) 133 | -------------------------------------------------------------------------------- /tests/haskell-string-tests.el: -------------------------------------------------------------------------------- 1 | ;; unit tests for haskell-string.el -*- lexical-binding: t -*- 2 | 3 | (require 'ert) 4 | 5 | (require 'haskell-string) ;; implementation under test 6 | 7 | (ert-deftest haskell-string-take () 8 | (should (string= (haskell-string-take "" 0) "")) 9 | (should (string= (haskell-string-take "" 1) "")) 10 | (should (string= (haskell-string-take "" 2) "")) 11 | (should (string= (haskell-string-take "x" 0) "")) 12 | (should (string= (haskell-string-take "x" 1) "x")) 13 | (should (string= (haskell-string-take "x" 2) "x")) 14 | (should (string= (haskell-string-take "x" 3) "x")) 15 | (should (string= (haskell-string-take "xy" 0) "")) 16 | (should (string= (haskell-string-take "xy" 1) "x")) 17 | (should (string= (haskell-string-take "xy" 2) "xy")) 18 | (should (string= (haskell-string-take "xy" 3) "xy")) 19 | (should (string= (haskell-string-take "xyz" 0) "")) 20 | (should (string= (haskell-string-take "xyz" 1) "x")) 21 | (should (string= (haskell-string-take "xyz" 2) "xy")) 22 | (should (string= (haskell-string-take "xyz" 3) "xyz")) 23 | (should (string= (haskell-string-take "xyz" 4) "xyz"))) 24 | 25 | (ert-deftest haskell-string-ellipsize () 26 | (should (string= (haskell-string-ellipsize "" 0) "")) 27 | (should (string= (haskell-string-ellipsize "" 1) "")) 28 | (should (string= (haskell-string-ellipsize "" 2) "")) 29 | (should (string= (haskell-string-ellipsize "x" 0) "")) 30 | (should (string= (haskell-string-ellipsize "x" 1) "x")) 31 | (should (string= (haskell-string-ellipsize "x" 2) "x")) 32 | (should (string= (haskell-string-ellipsize "x" 3) "x")) 33 | (should (string= (haskell-string-ellipsize "xy" 0) "")) 34 | (should (string= (haskell-string-ellipsize "xy" 1) "…")) 35 | (should (string= (haskell-string-ellipsize "xy" 2) "xy")) 36 | (should (string= (haskell-string-ellipsize "xy" 3) "xy")) 37 | (should (string= (haskell-string-ellipsize "xyz" 0) "")) 38 | (should (string= (haskell-string-ellipsize "xyz" 1) "…")) 39 | (should (string= (haskell-string-ellipsize "xyz" 2) "x…")) 40 | (should (string= (haskell-string-ellipsize "xyz" 3) "xyz")) 41 | (should (string= (haskell-string-ellipsize "xyz" 4) "xyz"))) 42 | 43 | (ert-deftest haskell-string-literal-encode-empty () 44 | (should (string= (haskell-string-literal-encode "") "\"\"")) 45 | (should (string= (haskell-string-literal-encode "" t) ""))) 46 | 47 | (ert-deftest haskell-string-literal-decode-empty () 48 | (dolist (s0 (list "\"\"" 49 | "\"\\&\"" 50 | "\"\\&\\&\\&\"" 51 | "\"\\ \\\"" 52 | "\"\\ \\\\ \\\"" 53 | "\"\\&\\ \\\"" 54 | "\"\\ \\\\&\\ \\\"")) 55 | (should (string= "" (haskell-string-literal-decode s0))) 56 | (should (string= "" (haskell-string-literal-decode (substring s0 1 -1) t))))) 57 | 58 | (ert-deftest haskell-string-literal-decode-backslash () 59 | "Test some edge cases involving backslashes." 60 | (dolist (cs (list (cons "\\\\" "\\") 61 | (cons "\\x10" "\x10") 62 | (cons "\\\\x10" "\\x10") 63 | (cons "\\ \\x10" "x10") 64 | (cons "\\ \\ \\x30" " 0") 65 | (cons "\\SO\\&H" "\x0eH") 66 | (cons "\\SOH\\&" "\x01") 67 | (cons "\\n" "\n") 68 | (cons "\\'" "'") 69 | (cons "\\\"" "\"") 70 | (cons "\\SOH" "\x01") 71 | (cons "\\1114111" "\x10ffff") 72 | (cons "\\o4177777" "\x10ffff") 73 | (cons "\\x10ffff" "\x10ffff") 74 | (cons "\\^@" "\x00") 75 | (cons "\\^A" "\x01") 76 | (cons "\\^Z" "\x1A") 77 | (cons "\\^[" "\x1B") 78 | (cons "\\^\\" "\x1C") 79 | (cons "\\^]" "\x1D") 80 | (cons "\\^^" "\x1E") 81 | (cons "\\^_" "\x1F"))) 82 | (should (string= (cdr cs) 83 | (haskell-string-literal-decode (concat "\"" (car cs) "\"")))) 84 | (should (string= (cdr cs) 85 | (haskell-string-literal-decode (car cs) t))))) 86 | 87 | (defun haskell-string-random (n) 88 | "Generate random N characters long string." 89 | (let ((a ())) 90 | (apply #'string (dotimes (_ n a) 91 | (setq a (cons (random 1024) a)))))) 92 | 93 | (ert-deftest haskell-string-literal-decode-encode () 94 | "Test whether decode+encode is the identity function." 95 | (random "c7430a4") 96 | ;; some edge cases 97 | (dolist (s0 (list "\x0e\x48" ;; '\SO' 'H' 98 | "\x01" ;; '\SOH' 99 | "\x00df\x30" ;; '\223' '0' 100 | "'" 101 | "\'" 102 | "\"" 103 | "\x0e&H" 104 | "\\" 105 | " \\ \\" 106 | "\\\\\"" 107 | (string 40 945 8322 946 8323 8743 947 178 949 178 41) 108 | "x" 109 | "xy" 110 | "\\x123" 111 | "\\ \\x123" 112 | " " 113 | " " 114 | "")) 115 | (should (string= s0 (haskell-string-literal-decode (haskell-string-literal-encode s0)))) 116 | (should (string= s0 (haskell-string-literal-decode (haskell-string-literal-encode s0 t) t)))) 117 | 118 | ;; randomized testing 119 | (dotimes (_ 50) 120 | (dotimes (n 15) 121 | (let* ((s0 (haskell-string-random (+ 1 n))) 122 | (s1 (haskell-string-literal-decode (haskell-string-literal-encode s0))) 123 | (s2 (haskell-string-literal-decode (haskell-string-literal-encode s0 t) t))) 124 | (should (string= s0 s1)) 125 | (should (string= s0 s2)))))) 126 | 127 | (ert-deftest haskell-string-test-trim () 128 | (should (equal "saf \t sdsaf" 129 | (haskell-string-trim "\r\n saf \t sdsaf \t\v\n \f"))) 130 | (should (haskell-string-only-spaces-p "\r\n \t \t\v\n \f")) 131 | (should-not (haskell-string-only-spaces-p "\r\n \t x \t\v\n \f"))) 132 | -------------------------------------------------------------------------------- /tests/haskell-test-utils-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-test-utils.el --- Utilities for Haskell Mode tests. -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2017 Vasantha Ganesh Kanniappan <vasanthaganesh.k@tuta.io> 4 | 5 | ;; This file is part of haskell-mode package. 6 | ;; You can contact with authors using GitHub issue tracker: 7 | ;; https://github.com/haskell/haskell-mode/issues 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file provides tests for `haskell-test-utils.el' 27 | 28 | (require 'haskell-test-utils) 29 | 30 | (ert-deftest haskell-with-temp-dir-structure-test () 31 | (setq cur-haskell-dir default-directory) 32 | (with-temp-dir-structure 33 | (("a.hs" . "-- Empty file") 34 | ("abc" . (("b.hs" . "-- Empty file")))) 35 | (cd "abc")) 36 | (should (eq default-directory cur-haskell-dir))) 37 | -------------------------------------------------------------------------------- /tests/haskell-utils-tests.el: -------------------------------------------------------------------------------- 1 | ;;; haskell-utils-tests.el --- Tests for Haskell utilities package -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2016 Arthur Fayzrakhmanov. All rights reserved. 4 | 5 | ;; This file is part of haskell-mode package. 6 | ;; You can contact with authors using GitHub issue tracker: 7 | ;; https://github.com/haskell/haskell-mode/issues 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This package provides regression tests for haskell-utils package. 27 | 28 | ;;; Code: 29 | 30 | (require 'ert) 31 | (require 'haskell-test-utils) 32 | (require 'haskell-utils) 33 | 34 | (ert-deftest simple-import-parse () 35 | (should (equal "A.B.C" 36 | (with-temp-buffer 37 | (insert-lines "import A.B.C") 38 | (goto-char (point-min)) 39 | (forward-line 0) 40 | (haskell-utils-parse-import-statement-at-point))))) 41 | 42 | (ert-deftest qualified-import-parse () 43 | (should (equal "A.B.C" 44 | (with-temp-buffer 45 | (insert-lines "import qualified A.B.C") 46 | (goto-char (point-min)) 47 | (forward-line 0) 48 | (haskell-utils-parse-import-statement-at-point))))) 49 | 50 | (ert-deftest qualified-as-import-parse () 51 | (should (equal "AAA.Bc.Cx" 52 | (with-temp-buffer 53 | (insert-lines "import qualified AAA.Bc.Cx as Something") 54 | (goto-char (point-min)) 55 | (forward-line 0) 56 | (haskell-utils-parse-import-statement-at-point))))) 57 | 58 | (ert-deftest international-characters-import-parse () 59 | (should (equal "Żółć" 60 | (with-temp-buffer 61 | (insert-lines "import Żółć") 62 | (goto-char (point-min)) 63 | (forward-line 0) 64 | (haskell-utils-parse-import-statement-at-point))))) 65 | 66 | (ert-deftest commented-out-import-parse () 67 | (should (equal nil 68 | (with-temp-buffer 69 | (insert-lines "-- import Nothing") 70 | (goto-char (point-min)) 71 | (forward-line 0) 72 | (haskell-utils-parse-import-statement-at-point))))) 73 | 74 | (ert-deftest non-import-import-parse () 75 | (should (equal nil 76 | (with-temp-buffer 77 | (insert-lines "something import Nothing") 78 | (goto-char (point-min)) 79 | (forward-line 0) 80 | (haskell-utils-parse-import-statement-at-point))))) 81 | 82 | (ert-deftest many-spaces-import-parse () 83 | (should (equal "M" 84 | (with-temp-buffer 85 | (insert-lines "\t import\t qualified \t\tM\tas G") 86 | (goto-char (point-min)) 87 | (forward-line 0) 88 | (haskell-utils-parse-import-statement-at-point))))) 89 | 90 | (ert-deftest using-underscores-import-parse () 91 | (should (equal "Module_1.S_3_3_" 92 | (with-temp-buffer 93 | (insert-lines "import Module_1.S_3_3_") 94 | (goto-char (point-min)) 95 | (forward-line 0) 96 | (haskell-utils-parse-import-statement-at-point))))) 97 | 98 | (ert-deftest slightly-malformed-import-parse () 99 | (should (equal "q.Module...qwerqwe..." 100 | (with-temp-buffer 101 | (insert-lines "import q.Module...qwerqwe...") 102 | (goto-char (point-min)) 103 | (forward-line 0) 104 | (haskell-utils-parse-import-statement-at-point))))) 105 | 106 | (ert-deftest package-import-parse () 107 | (should (equal "B" 108 | (with-temp-buffer 109 | (insert-lines "import \"package-1.2.3\" B") 110 | (goto-char (point-min)) 111 | (forward-line 0) 112 | (haskell-utils-parse-import-statement-at-point))))) 113 | 114 | (ert-deftest safe-haskell-import-parse () 115 | (should (equal "B" 116 | (with-temp-buffer 117 | (insert-lines "import safe B") 118 | (goto-char (point-min)) 119 | (forward-line 0) 120 | (haskell-utils-parse-import-statement-at-point))))) 121 | 122 | (ert-deftest full-import-parse () 123 | (should (equal "Data.Char.Unicode_v_7" 124 | (with-temp-buffer 125 | (insert-lines "import safe qualified \"unicode-7.0\" Data.Char.Unicode_v_7 as U (func)") 126 | (goto-char (point-min)) 127 | (forward-line 0) 128 | (haskell-utils-parse-import-statement-at-point))))) 129 | 130 | (ert-deftest type-at-command-composition () 131 | "Test `haskell-utils-compose-type-at-command'. 132 | Test only position conversion to line and column numbers, do not 133 | test last string compontent, it is used in `:type-at` command to 134 | provide user friendly output only and could be any string, even 135 | empty one. Very likely the way how its composed for multilne 136 | strings will change in future." 137 | (with-temp-buffer 138 | (insert-lines "module A where" 139 | "" 140 | "int :: Int" 141 | "int = 369" 142 | "" 143 | "act =" 144 | " do print int" 145 | " return int") 146 | (goto-char (point-min)) 147 | (let (test-a-points 148 | test-b-points 149 | test-a-result 150 | test-b-result) 151 | ;; go to third line, e.g. `int` definition 152 | (forward-line 3) 153 | (setq test-a-points (point)) 154 | ;; go to at the end of `int` definition, i.e. point stands at whitespace 155 | (forward-char 3) 156 | (setq test-a-points `(,test-a-points . ,(point))) 157 | (goto-char (line-beginning-position)) 158 | ;; go to do-block line 159 | (forward-line 3) 160 | ;; go to `do` keyword beginning 161 | (forward-char 2) 162 | (setq test-b-points (point)) 163 | ;; go to the end of do-block 164 | (goto-char (point-max)) 165 | (setq test-b-points `(,test-b-points . ,(point))) 166 | (setq test-a-result 167 | (haskell-utils-compose-type-at-command test-a-points)) 168 | (setq test-b-result 169 | (haskell-utils-compose-type-at-command test-b-points)) 170 | (should (string-prefix-p ":type-at nil 4 1 4 4" test-a-result)) 171 | (should (string-prefix-p ":type-at nil 7 3 8 16" test-b-result))))) 172 | 173 | (ert-deftest parse-repl-response () 174 | "Test `haskell-utils-repl-response-error-status' function." 175 | (let* ((t1-str "unknown command ':type-at'\nuse :? for help.") 176 | (t2-str "\n<interactive>:3:5: Not in scope: ‘x’") 177 | (t3-str "Couldn't guess that module name. Does it exist?") 178 | (t4-str "Hello World!") 179 | (t5-str " ") 180 | (t6-str "") 181 | (t7-str "\n\n\n\n") 182 | (r1 (haskell-utils-repl-response-error-status t1-str)) 183 | (r2 (haskell-utils-repl-response-error-status t2-str)) 184 | (r3 (haskell-utils-repl-response-error-status t3-str)) 185 | (r4 (haskell-utils-repl-response-error-status t4-str)) 186 | (r5 (haskell-utils-repl-response-error-status t5-str)) 187 | (r6 (haskell-utils-repl-response-error-status t6-str)) 188 | (r7 (haskell-utils-repl-response-error-status t7-str))) 189 | (should (equal r1 'unknown-command)) 190 | (should (equal r2 'interactive-error)) 191 | (should (equal r3 'option-missing)) 192 | (should (equal r4 'no-error)) 193 | (should (equal r5 'no-error)) 194 | (should (equal r6 'no-error)) 195 | (should (equal r7 'no-error)))) 196 | 197 | (ert-deftest reduce-strign () 198 | "Test `haskell-utils-reduce-strign' command. 199 | Whitespace sequences at beginning of lines should be replaced 200 | with single whitespace, all newline characters should be 201 | removed." 202 | (should (string-equal "" (haskell-utils-reduce-string "\n"))) 203 | (should (string-equal "" (haskell-utils-reduce-string "\r\n"))) 204 | (should (string-equal " " (haskell-utils-reduce-string " \n"))) 205 | (should (string-equal 206 | "TestTest" 207 | (haskell-utils-reduce-string "Test\nTest"))) 208 | (should (string-equal 209 | "Test Test" 210 | (haskell-utils-reduce-string "Test\n Test"))) 211 | (should (string-equal 212 | " Test Test" 213 | (haskell-utils-reduce-string " Test\r\n Test"))) 214 | (should (string-equal 215 | " TestTest" 216 | (haskell-utils-reduce-string " Test\r\nTest"))) 217 | (should (string-equal 218 | " TestTest Test test" 219 | (haskell-utils-reduce-string " Test\r\nTest\n Test test")))) 220 | 221 | (ert-deftest post-command-hooks () 222 | "Test commands related `haskell-utils-async-post-command-flag'. 223 | Tests flag updates and `post-command-hook' cleanup." 224 | (with-temp-switch-to-buffer 225 | ;; set some random value to flag to test that it will be reset 226 | (setq haskell-utils-async-post-command-flag "non nil") 227 | (haskell-utils-async-watch-changes) 228 | ;; now flag should be empty 229 | (should (null haskell-utils-async-post-command-flag)) 230 | ;; execute some commands 231 | (save-excursion (insert "Hello World!")) 232 | (execute-kbd-macro (kbd "SPC")) 233 | ;; now flag should not be empty 234 | (should (not (null haskell-utils-async-post-command-flag))) 235 | ;; check that hook was installed 236 | (should (cl-member #'haskell-utils-async-update-post-command-flag 237 | post-command-hook 238 | :test #'equal)) 239 | ;; check that flag was cleaned up 240 | (haskell-utils-async-stop-watching-changes (current-buffer)) 241 | (should (null haskell-utils-async-post-command-flag)) 242 | ;; check that hook was removed 243 | (should (not (cl-member #'haskell-utils-async-update-post-command-flag 244 | post-command-hook 245 | :test #'equal))))) 246 | 247 | ;;; haskell-utils-tests.el ends here 248 | -------------------------------------------------------------------------------- /tests/inferior-haskell-tests.el: -------------------------------------------------------------------------------- 1 | ;;; inferior-haskell-tests.el --- tests for collapse module -*- lexical-binding: t -*- 2 | 1;4803;0c 3 | ;; Copyright © 2017 Vasantha Ganesh K. <vasanthaganesh.k@tuta.io> 4 | 5 | ;; This file is not part of GNU Emacs. 6 | 7 | ;; This file is free software; you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation; either version 3, or (at your option) 10 | ;; any later version. 11 | 12 | ;; This file is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 19 | 20 | 21 | (require 'ert) 22 | (require 'inf-haskell) 23 | (require 'haskell-string) 24 | (require 'haskell-test-utils) 25 | 26 | (ert-deftest test-run-haskell () 27 | (haskell-unconditional-kill-buffer "*haskell*") 28 | (run-haskell) 29 | (let* ((times 5) 30 | (ans nil)) 31 | (setq ans (inferior-haskell-get-result "1 + 1")) 32 | (while (and (> times 0) 33 | (not (equal ans "2"))) 34 | (setq times (1- times)) 35 | (setq ans (inferior-haskell-get-result "1 + 1"))) 36 | (should (equal ans 37 | "2")))) 38 | 39 | (ert-deftest test-inferior-haskell-buffer () 40 | "Check if the inferior haskell buffer has been started" 41 | (haskell-unconditional-kill-buffer "*haskell*") 42 | (run-haskell) 43 | (should (buffer-live-p inferior-haskell-buffer))) 44 | 45 | (ert-deftest test-inferior-haskell-root-dir () 46 | "Check if the root dir of the loaded file/project is not nil 47 | This way we test is the file is loaded or not" 48 | (haskell-unconditional-kill-buffer "*haskell*") 49 | (run-haskell) 50 | (should (file-directory-p inferior-haskell-root-dir))) 51 | -------------------------------------------------------------------------------- /tests/interactive-haskell-mode-tests.el: -------------------------------------------------------------------------------- 1 | ;;; interactive-haskell-mode-tests.el --- Tests for Haskell Interactive Mode -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2016 Athur Fayzrakhmanov. All rights reserved. 4 | 5 | ;; This file is part of haskell-mode package. 6 | ;; You can contact the authors using GitHub issue tracker: 7 | ;; https://github.com/haskell/haskell-mode/issues 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This package provides regression tests for the package 27 | ;; haskell-interactive-mode. 28 | 29 | ;;; Code: 30 | 31 | 32 | (require 'ert) 33 | (require 'haskell-interactive-mode) 34 | 35 | (defun should-match (str) 36 | (should (eq 0 (string-match-p haskell-interactive-mode-error-regexp str)))) 37 | 38 | (ert-deftest haskell-interactive-error-regexp-test () 39 | "Tests the regexp `haskell-interactive-mode-error-regexp'" 40 | (should (eq 0 (string-match-p haskell-interactive-mode-error-regexp 41 | "/home/user/Test.hs:24:30:"))) 42 | (should (eq 0 (string-match-p haskell-interactive-mode-error-regexp 43 | "Test.hs:5:18:"))) 44 | (should (eq 0 (string-match-p haskell-interactive-mode-error-regexp 45 | "Test.hs:7:6: Not in scope: type constructor or class ‘Ty’"))) 46 | (should (eq 0 (string-match-p haskell-interactive-mode-error-regexp 47 | "Test.hs:9:5: Not in scope: ‘c’"))) 48 | (should (eq nil (string-match-p haskell-interactive-mode-error-regexp 49 | ;; leading space 50 | " Test.hs:8:9:"))) 51 | ) 52 | -------------------------------------------------------------------------------- /tests/test-data/Test.cabal: -------------------------------------------------------------------------------- 1 | Name: Test 2 | Version: 2999.18.0.1 3 | Stability: Beta 4 | Synopsis: Testing haskell-mode 5 | Description: { 6 | This is test .cabal file 7 | } 8 | 9 | Build-Type: Simple 10 | Tested-With: GHC == 7.10.2 11 | Cabal-Version: >= 1.14 12 | Extra-Source-Files: TODO.md 13 | Changelog.md 14 | README.md 15 | FAQ.md 16 | utils/AttributeGenerator.hs 17 | 18 | Source-Repository head 19 | Type: git 20 | Location: git://github.com/haskell-mode 21 | 22 | Flag some-flag 23 | Description: This is a flag 24 | Default: False 25 | 26 | Library 27 | Default-Language: Haskell2010 28 | 29 | Build-Depends: base == 4.*, 30 | containers, 31 | process, 32 | directory, 33 | Exposed-Modules: Some.Module 34 | Other-Modules: Some.Other.Module 35 | Ghc-Options: -Wall 36 | 37 | 38 | Test-Suite test-1 { 39 | Default-Language: Haskell2010 40 | Type: exitcode-stdio-1.0 41 | Build-Depends: base, 42 | containers, 43 | fgl, 44 | fgl-arbitrary == 0.2.*, 45 | filepath, 46 | text, 47 | QuickCheck >= 2.3 && < 2.9 48 | hs-Source-Dirs: tests 49 | Main-Is: RunTests.hs 50 | Other-Modules: Some.Module 51 | Ghc-Options: -O -Wall 52 | } 53 | 54 | Benchmark bench-1 { 55 | Default-Language: Haskell2010 56 | Type: exitcode-stdio-1.0 57 | Build-Depends: base, 58 | deepseq, 59 | text, 60 | criterion >= 0.5 && < 1.2 61 | hs-Source-Dirs: utils 62 | Main-Is: Benchmark.hs 63 | } 64 | 65 | Executable bin-1 66 | Default-Language: Haskell2010 67 | hs-Source-Dirs: utils 68 | Main-Is: TestParsing.hs 69 | Build-Depends: base, 70 | bytestring, 71 | directory, 72 | filepath, 73 | text 74 | Ghc-Options: -O -Wall 75 | -------------------------------------------------------------------------------- /w3m-haddock.el: -------------------------------------------------------------------------------- 1 | ;;; w3m-haddock.el --- Make browsing haddocks with w3m-mode better -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2014 Chris Done 4 | 5 | ;; Author: Chris Done <chrisdone@gmail.com> 6 | 7 | ;; This file is not part of GNU Emacs. 8 | 9 | ;; This file is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; This file is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to 21 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;;; Code: 27 | 28 | (require 'cl-lib) 29 | (require 'haskell-mode) 30 | (require 'haskell-font-lock) 31 | 32 | (declare-function w3m-buffer-title "ext:w3m") 33 | (declare-function w3m-browse-url "ext:w3m") 34 | (defvar w3m-current-url) 35 | 36 | (add-hook 'w3m-display-hook 'w3m-haddock-display) 37 | 38 | (defface w3m-haddock-heading-face 39 | '((((class color)) :inherit highlight)) 40 | "Face for quarantines." 41 | :group 'haskell) 42 | 43 | (defcustom haskell-w3m-haddock-dirs 44 | '("~/.cabal/share/doc/") 45 | "The path to your cabal documentation dir. 46 | It should contain directories of package-name-x.x. 47 | 48 | You can rebind this if you're using hsenv by adding it to your 49 | .dir-locals.el in your project root. E.g. 50 | 51 | ((haskell-mode . 52 | ((haskell-w3m-haddock-dirs . 53 | (\"/home/chris/Projects/foobar/.hsenv/cabal/share/doc\"))))) 54 | 55 | " 56 | :group 'haskell 57 | :type '(list string)) 58 | 59 | (defvar w3m-haddock-entry-regex "^\\(\\(data\\|type\\) \\|[a-z].* :: \\)" 60 | "Regex to match entry headings.") 61 | 62 | (defun haskell-w3m-open-haddock () 63 | "Open a haddock page in w3m." 64 | (interactive) 65 | (let* ((entries (cl-remove-if (lambda (s) (string= s "")) 66 | (apply 'append (mapcar (lambda (dir) 67 | (split-string (shell-command-to-string (concat "ls -1 " dir)) 68 | 69 | "\n")) 70 | haskell-w3m-haddock-dirs)))) 71 | (package-dir (ido-completing-read 72 | "Package: " 73 | entries))) 74 | (cond 75 | ((member package-dir entries) 76 | (unless (cl-loop for dir in haskell-w3m-haddock-dirs 77 | when (w3m-haddock-find-index dir package-dir) 78 | do (progn (w3m-browse-url (w3m-haddock-find-index dir package-dir) 79 | t) 80 | (cl-return t))) 81 | (w3m-browse-url (concat "http://hackage.haskell.org/package/" 82 | package-dir) 83 | t))) 84 | (t 85 | (w3m-browse-url (concat "http://hackage.haskell.org/package/" 86 | package-dir) 87 | t))))) 88 | 89 | (defun w3m-haddock-find-index (dir package) 90 | (let ((html-index (concat dir "/" package "/html/index.html")) 91 | (index (concat dir "/" package "/index.html"))) 92 | (cond 93 | ((file-exists-p html-index) 94 | html-index) 95 | ((file-exists-p index) 96 | index)))) 97 | 98 | (defun w3m-haddock-page-p () 99 | "Haddock general page?" 100 | (save-excursion 101 | (goto-char (point-max)) 102 | (forward-line -2) 103 | (looking-at "[ ]*Produced by Haddock"))) 104 | 105 | (defun w3m-haddock-source-p () 106 | "Haddock source page?" 107 | (save-excursion 108 | (goto-char (point-min)) 109 | (or (looking-at "Location: https?://hackage.haskell.org/package/.*/docs/src/") 110 | (looking-at "Location: file://.*cabal/share/doc/.*/html/src/") 111 | (looking-at "Location: .*src/.*.html$")))) 112 | 113 | (defun w3m-haddock-p () 114 | "Any haddock page?" 115 | (or (w3m-haddock-page-p) 116 | (w3m-haddock-source-p))) 117 | 118 | (defun w3m-haddock-find-tag () 119 | "Find a tag by jumping to the \"All\" index and doing a 120 | search-forward." 121 | (interactive) 122 | (when (w3m-haddock-p) 123 | (let ((ident (haskell-ident-at-point))) 124 | (when ident 125 | (w3m-browse-url 126 | (replace-regexp-in-string "docs/.*" "docs/doc-index-All.html" w3m-current-url)) 127 | (search-forward ident))))) 128 | 129 | (defun w3m-haddock-display (_url) 130 | "To be run by w3m's display hook. This takes a normal w3m 131 | buffer containing hadddock documentation and reformats it to be 132 | more usable and look like a dedicated documentation page." 133 | (when (w3m-haddock-page-p) 134 | (save-excursion 135 | (goto-char (point-min)) 136 | (let ((inhibit-read-only t)) 137 | (delete-region (point) 138 | (line-end-position)) 139 | (w3m-haddock-next-heading) 140 | ;; Start formatting entries 141 | (while (looking-at w3m-haddock-entry-regex) 142 | (when (w3m-haddock-valid-heading) 143 | (w3m-haddock-format-heading)) 144 | (w3m-haddock-next-heading)))) 145 | (rename-buffer (concat "*haddock: " (w3m-buffer-title (current-buffer)) "*"))) 146 | (when (w3m-haddock-source-p) 147 | (font-lock-mode -1) 148 | (let ((n (line-number-at-pos))) 149 | (save-excursion 150 | (goto-char (point-min)) 151 | (forward-line 1) 152 | (let ((text (buffer-substring (point) 153 | (point-max))) 154 | (inhibit-read-only t)) 155 | (delete-region (point) 156 | (point-max)) 157 | (insert 158 | (haskell-fontify-as-mode text 159 | 'haskell-mode)))) 160 | (goto-char (point-min)) 161 | (forward-line (1- n))))) 162 | 163 | (defun w3m-haddock-format-heading () 164 | "Format a haddock entry." 165 | (let ((o (make-overlay (line-beginning-position) 166 | (1- (save-excursion (w3m-haddock-header-end)))))) 167 | (overlay-put o 'face 'w3m-haddock-heading-face)) 168 | (let ((end (save-excursion 169 | (w3m-haddock-next-heading) 170 | (when (w3m-haddock-valid-heading) 171 | (point))))) 172 | (when end 173 | (save-excursion 174 | (w3m-haddock-header-end) 175 | (indent-rigidly (point) 176 | end 177 | 4))))) 178 | 179 | (defun w3m-haddock-next-heading () 180 | "Go to the next heading, or end of the buffer." 181 | (forward-line 1) 182 | (or (search-forward-regexp w3m-haddock-entry-regex nil t 1) 183 | (goto-char (point-max))) 184 | (goto-char (line-beginning-position))) 185 | 186 | (defun w3m-haddock-valid-heading () 187 | "Is this a valid heading?" 188 | (not (get-text-property (point) 'face))) 189 | 190 | (defun w3m-haddock-header-end () 191 | "Go to the end of the header." 192 | (search-forward-regexp "\n[ \n]")) 193 | 194 | (provide 'w3m-haddock) 195 | ;;; w3m-haddock.el ends here 196 | --------------------------------------------------------------------------------