Initial commit
This commit is contained in:
600
elpa/haskell-mode-20171022.26/NEWS
Normal file
600
elpa/haskell-mode-20171022.26/NEWS
Normal file
@@ -0,0 +1,600 @@
|
||||
Haskell Mode NEWS -*- org -*-
|
||||
|
||||
This file uses Org mode. Some useful (default) key-bindings:
|
||||
- Use "C-c C-n"/"C-c C-p" to jump to next/prev heading
|
||||
- Use "<tab>" to expand/collapse nodes
|
||||
- Use "<backtab>" to cycle visibility of all nodes
|
||||
- Use "C-c C-o" to open links
|
||||
|
||||
* Changes in 16.1
|
||||
|
||||
- Require at least Emacs 24.3
|
||||
|
||||
- Implemented standalone deriving indentation
|
||||
|
||||
- Removed haskell-indentation-ifte-offset
|
||||
|
||||
- Implemented electric characters
|
||||
|
||||
- Added LiquidHaskell annotation highlight
|
||||
|
||||
- Introduced haskell-hasktags-path defcustom
|
||||
|
||||
- Added font lock tests for pattern synonyms
|
||||
|
||||
- Hardcoded haskell-ghc-supported-extensions/options
|
||||
|
||||
- Added multi-line input in haskell-interactive
|
||||
|
||||
- Added haskell-mode-stylish-haskell-path
|
||||
|
||||
- Added Windows CI builds
|
||||
|
||||
- Added syntax highlight to Yesod rules quasi quote
|
||||
|
||||
- Added support for line continuation in #define's
|
||||
|
||||
- Added support unterminated strings in indentation
|
||||
|
||||
- Implemented /italic/ and *bold* for haddock
|
||||
|
||||
- Added significant speedups in font-lock engine
|
||||
|
||||
- Added support for full complexity of backtick syntax
|
||||
|
||||
- Operators at the end or beggining of line force expression continuation in indentation
|
||||
|
||||
- Map haskell-mode-format-imports to C-c C-, to avoid conflict with haskell-indent
|
||||
|
||||
- Improve auto add dependencies in cabal
|
||||
|
||||
- Fix inferior-haskell warning font lock for GHC 8.0
|
||||
|
||||
* Changes in 13.20
|
||||
|
||||
- Require at least Emacs 24.1
|
||||
|
||||
- Honor equals on its own line in data decl
|
||||
|
||||
- Honor equals on separate line after guards
|
||||
|
||||
- Allow haskell-process-path-* to be lists
|
||||
|
||||
- Fontify True/False in cabal mode
|
||||
|
||||
- Align | with = in data declarations
|
||||
|
||||
- Remove haskell-package.el
|
||||
|
||||
- Run stylish-haskell before save
|
||||
|
||||
- Improved lexeme parsing in haskell-lexeme
|
||||
|
||||
- Add haskell-interactive-copy-to-prompt
|
||||
|
||||
- Remove haskell-mode-contextual-space and related var
|
||||
|
||||
- Make haskell-indent-region do nothing
|
||||
|
||||
- Add c2hs mode
|
||||
|
||||
- Improve hasktags handling
|
||||
|
||||
- Move documentation from wiki to haskell-mode manual
|
||||
|
||||
* Changes in 13.18
|
||||
|
||||
- Removed haskell-bot
|
||||
|
||||
- Implement shallow indentation
|
||||
|
||||
- Removed haskell-simple-indent
|
||||
|
||||
- Removed haskell-indentation show dynamic positions
|
||||
|
||||
- Improved indentation inside multiline strings
|
||||
|
||||
- Implemented font-lock for quasi quoted XML, HTML and JavaScript
|
||||
|
||||
- Added '{type|data} family' to font-lock-keywords-create
|
||||
|
||||
- Started using undercover.el for emacs lisp coverage
|
||||
|
||||
- Added keybinding for `haskell-cabal-visit-file`
|
||||
|
||||
- Added type role to font lock
|
||||
|
||||
- Detect and respect the comma style of a section in cabal files
|
||||
|
||||
* Changes in 13.16
|
||||
|
||||
- Improved parsing of comma lists in haskell indentation
|
||||
|
||||
- Declared turn-on-haskell-indentation as obsolete
|
||||
|
||||
- Improved QuasiQuote font lock
|
||||
|
||||
- Fixed indentation for Unicode symbols
|
||||
|
||||
- Improved completion engine
|
||||
|
||||
- Added support for interaction with stack ghci
|
||||
|
||||
- Added haskell-forward-sexp
|
||||
|
||||
- Added overlays to haskell load
|
||||
|
||||
- Made use of lexical binding for all emacs lisp sources
|
||||
|
||||
- Improved indentation of guards with commas
|
||||
|
||||
- Made three indentation modes mutually turn each other off
|
||||
|
||||
- Added an additional trigger for pragma suggestions
|
||||
|
||||
- Improved and documented `toggle-scc-at-point` in manual
|
||||
|
||||
* Changes in 13.14
|
||||
|
||||
- Add official Haskell Mode logo
|
||||
|
||||
- Add auto deploy for html manual
|
||||
|
||||
- Improve presentation mode
|
||||
|
||||
- Font Lock refactoring and improvements
|
||||
|
||||
- Properly delimit operators in prettify mode
|
||||
|
||||
- OPTIONS and LANGUAGE completion using ghci
|
||||
|
||||
- Merge hi2 (haskell-indentation attempt 2) in place of haskell-indentation
|
||||
|
||||
- Prompt to reconfigure when Cabal demands it
|
||||
|
||||
- Fontify pragmas while fontifying comments
|
||||
|
||||
- Operators are fontified
|
||||
|
||||
- Remove all mentions of cabal-dev
|
||||
|
||||
- Merge hsc-mode into haskell-mode
|
||||
|
||||
- Get type information from ghci in interaction-mode
|
||||
|
||||
- Prettify Haskell types in eldoc support
|
||||
|
||||
- Added haskell-hoogle-url
|
||||
|
||||
- Fix w3m-haddock in the case of no local files
|
||||
|
||||
* Changes in 13.12
|
||||
|
||||
- Added haskell-bot.el
|
||||
|
||||
- Added support for cabal repl build targets
|
||||
|
||||
- Automatically add import lines via Hoogle
|
||||
|
||||
- Automatically add package to cabal file
|
||||
|
||||
- Added w3m-haddock.el
|
||||
|
||||
- Added debugger mode
|
||||
|
||||
- Added preliminary :present support
|
||||
|
||||
- Added haskell-sort-imports
|
||||
|
||||
- Added haskell-complete-module
|
||||
|
||||
- Support if and multi-way if in indentation
|
||||
|
||||
- Add support to generate tags on windows
|
||||
|
||||
- Add haskell-language-extensions variable
|
||||
|
||||
- Improve haskell-simple-indent mode
|
||||
|
||||
- Improve test cases
|
||||
|
||||
* Changes in 13.10
|
||||
|
||||
- Small fix for haskell-simple-indent: Certain indentation situations
|
||||
cause valname-string to be nil, which haskell-trim did not handle
|
||||
gracefully (naturally, since nil != "").
|
||||
|
||||
- Luke Hoersten's Shnippet merged in under snippets/.
|
||||
|
||||
- haskell-presentation-mode is now a haskell-mode derived mode.
|
||||
|
||||
- Small improvement to haskell-process-do-info (works on constructors
|
||||
now and underscored names).
|
||||
|
||||
- Add haskell-indent-spaces configuration variable.
|
||||
|
||||
- The command string to run cabal commands is slightly more
|
||||
configurable. See: C-h f haskell-process-do-cabal-format-string
|
||||
|
||||
* Changes in 13.8
|
||||
|
||||
See also [[https://github.com/haskell/haskell-mode/compare/v13.07...v13.08][detailed Git history]].
|
||||
|
||||
- Make `haskell-simple-indent-mode' a proper minor mode with `SInd` as
|
||||
mode-line lighter
|
||||
|
||||
- Support popular "λ> " prompt in inf-haskell by default
|
||||
|
||||
- Hide internal `*print-haskell-mode*' buffers
|
||||
(used when `haskell-interactive-mode-eval-mode' is active)
|
||||
|
||||
- Add tab-completion support for haskell-interactive-mode
|
||||
(requires `:complete' command support in GHCi)
|
||||
|
||||
- Add support to `haskell-process-do-info` to perform `:browse!` query
|
||||
on module name when called on import statement line
|
||||
|
||||
- `haskell-decl-scan-mode':
|
||||
- New customize group `haskell-decl-scan'
|
||||
- New flag `haskell-decl-scan-bindings-as-variables' for controlling
|
||||
whether to put value bindings into the "Variables" category.
|
||||
- New flag `haskell-decl-scan-add-to-menubar' for controlling
|
||||
whether to add "Declarations" menu entry to menu bar.
|
||||
- New manual section node `(haskell-mode)haskell-decl-scan-mode'
|
||||
|
||||
- Add support for [[http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#lambda-case][LambdaCase]] syntax extension to `haskell-indentation`
|
||||
|
||||
- Change `haskell-indentation-mode' to never jump back a whole line
|
||||
when pressing DEL. The old behavior can be restored by setting
|
||||
`haskell-indentation-delete-backward-jump-line' to t
|
||||
|
||||
- New convenience function `haskell-cabal-visit-file' for locating and
|
||||
visiting most likely `.cabal` file associated with current buffer
|
||||
|
||||
- Add support for [[http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#package-import][PackageImports]] and [[http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#safe-imports-ext][SafeHaskell]] syntax extensions to
|
||||
`haskell-decl-scan-mode' parser
|
||||
|
||||
- Add `turn-{on,off}-haskell-doc' commands as aliases for the existing
|
||||
`turn-{on,off}-haskell-doc-mode' commands
|
||||
|
||||
- Add support for "cabal repl" process type to `haskell-interactive-mode'
|
||||
|
||||
- Add new Haskell compilation sub-mode and associated `haskell-compile'
|
||||
command
|
||||
|
||||
* Changes in 13.7
|
||||
|
||||
See also [[https://github.com/haskell/haskell-mode/compare/v13.06...v13.07][detailed Git history]].
|
||||
|
||||
- Convert NEWS (this file) to Org mode style and include NEWS file in
|
||||
package and add command for visiting NEWS file
|
||||
(M-x haskell-mode-view-news)
|
||||
|
||||
- Officially drop support for versions prior to Emacs 23
|
||||
|
||||
- New work-in-progress Info manual for haskell-mode
|
||||
|
||||
- Remove deprecated `haskell-{hugs,ghci}' modules
|
||||
|
||||
- Font-locking changes:
|
||||
- Remove deprecated `turn-on-haskell-font-lock` function
|
||||
- Improve font-locking of type-signatures in presence of newlines
|
||||
- Use `font-lock-preprocessor-face' instead of the previously used
|
||||
`font-lock-warning-face` for CPP directives
|
||||
- Use `font-lock-warning-face` instead of the previously used
|
||||
`font-lock-preprocessor-face` for Git merge conflict annotations.
|
||||
|
||||
- Improvements to `haskell-move-nested' module:
|
||||
- Add support for operating on active regions
|
||||
- New interactive commands `haskell-move-nested-{left,right}` which
|
||||
support numeric prefix arguments for controlling the amount of
|
||||
shifting to apply.
|
||||
|
||||
- Add `haskell-unicode-input-method.el` to distribution
|
||||
(enable with `turn-on-haskell-unicode-input-method`)
|
||||
|
||||
- Fix all byte-compilation warnings
|
||||
|
||||
- Build-system:
|
||||
- For in-place installation, `haskell-site-file.el' is renamed
|
||||
to `haskell-mode-autoloads.el`
|
||||
- Auto-generate ELPA compatible README file by extracting header of
|
||||
haskell-mode.el
|
||||
- New "make check" target
|
||||
- Add Travis-CI build jobs for testing byte-compilation with
|
||||
multiple Emacs versions
|
||||
|
||||
- Reorganize customize settings
|
||||
- Add new convenience function for browsing all Haskell Mode settings
|
||||
(M-x haskell-customize)
|
||||
- Add `:link' keywords pointing to the new Info manual
|
||||
- Add `:group' keywords to modes to make (M-x customize-mode) work
|
||||
- Create new customization groups `haskell-interactive' and `inferior-haskell'
|
||||
to clean up namespace
|
||||
- Create new customization group `ghc-core` containing the two new
|
||||
customization variables `ghc-core-program` and `ghc-core-program-args`.
|
||||
|
||||
- Improvements to haskell-interactive-mode
|
||||
- Add support for deleting compile messages superseded by recompile/reloads
|
||||
(M-x customize-variable RET haskell-interactive-mode-delete-superseded-errors)
|
||||
- Fix `C-u M-x haskell-process-do-type` inserting bad signatures
|
||||
- Integrate with Emacs' `next-error` subsystem
|
||||
- Add "C-c C-f" binding to REPL keymap for enabling `next-error-follow-minor-mode'
|
||||
- Add support for `-ferror-spans`-style compile messages
|
||||
- Add `-ferror-spans` as default for `haskell-process-args-ghci`
|
||||
- Add optional argument to
|
||||
`haskell-session-{all,installed,project}-modules' to suppress
|
||||
session-creation. This is useful for yasnippet usage, see commit
|
||||
517fd7e for an example.
|
||||
- Change default for `haskell-process-path-ghci` to a static "ghci"
|
||||
- Fix `haskell-interactive-switch` not selecting the REPL window
|
||||
- Make `*haskell-process-log*` buffer configurable
|
||||
(controlled via new `haskell-process-log` customize option)
|
||||
|
||||
* Changes in 13.6
|
||||
|
||||
See also [[https://github.com/haskell/haskell-mode/compare/2_9_1...v13.06][detailed Git history]].
|
||||
|
||||
- Switch to new versioning scheme
|
||||
|
||||
- Switch to MELPA/Marmalade based packaging
|
||||
|
||||
- Cleanup/refactor build-system
|
||||
|
||||
- Enhance `M-x haskell-version` to report more detailed versioning
|
||||
information
|
||||
|
||||
- Make haskell-interactive-mode emulate comint/eshell history navigation
|
||||
(see commit 0e96843 for more details)
|
||||
|
||||
- Improvements to haskell-interactive-mode
|
||||
- Improve killing/restarting haskell-interactive sessions
|
||||
- Improve directory prompting and resolution
|
||||
- Fix redundant-import suggest trigger to support qualified imports
|
||||
- Detect all abbreviations of an user-inputted ":quit"
|
||||
- Fix regexps for recent GHC 7.x compiler messages
|
||||
- Customizable commandline args for GHCi
|
||||
(M-x customize-variable RET haskell-process-args-ghci)
|
||||
- New command to load or reload via prefix argument
|
||||
(M-x haskell-process-load-or-reload)
|
||||
- Fix haskell-interactive-mode prompt detection
|
||||
- Add cabal-ghci as supported process mode
|
||||
- Add a customization option for the visibility of multi-line errors
|
||||
(M-x customize-variable RET haskell-interactive-mode-hide-multi-line-errors)
|
||||
|
||||
- Add forward declarations to reduce Elisp bytecompile warnings
|
||||
|
||||
- Improvements to `haskell-indentation`
|
||||
- Add support for the UnicodeSyntax tokens `→`, `←`, and `∷`.
|
||||
- Indent "=" following data/type/newtype declarations.
|
||||
- Align "->"/"→" arrows in types under "::"/"∷"
|
||||
- Make customizable whether "<backspace>" deletes indentation too
|
||||
(via `haskell-indentation-delete-backward-indentation` and
|
||||
`haskell-indentation-delete-indentation`)
|
||||
- Properly indent 'rec' keyword, same as 'mdo'
|
||||
- Minor optimizations.
|
||||
|
||||
- Add support for "'"-prefixed constructors (-> DataKinds) to font-locking
|
||||
|
||||
- New experimental haskell session menu mode (M-x haskell-menu)
|
||||
|
||||
- Various minor cleanups/fixes/improvements...
|
||||
|
||||
* Changes in 2.9.1
|
||||
|
||||
See also [[https://github.com/haskell/haskell-mode/compare/2_9_0...2_9_1][detailed Git history]].
|
||||
|
||||
- Bugfix release adding missing autoload declaration
|
||||
|
||||
* Changes in 2.9.0
|
||||
|
||||
See also [[https://github.com/haskell/haskell-mode/compare/2_8_0...2_9_0][detailed Git history]].
|
||||
|
||||
- This is the first release after haskell-mode was migrated to GitHub
|
||||
|
||||
- New experimental `haskell-interactive-mode' module implementing a
|
||||
new REPL interaction mode for GHCi sessions to eventually replace
|
||||
the existing "inf-haskell" mode.
|
||||
|
||||
- New `haskell-process-cabal' command for interaction with cabal-install
|
||||
|
||||
- New `haskell-checkers' module
|
||||
|
||||
- Update haskell-cabal-mode font-lock keywords
|
||||
|
||||
- Improve scrolling of hoogle output (haskell-mode.el)
|
||||
|
||||
- Derive `haskell-mode` from `prog-mode` for Emacs 24+
|
||||
|
||||
- Add new binding for "<backtab>" to haskell-mode's keymap which
|
||||
unindents current line
|
||||
|
||||
- New modules `haskell-navigate-imports`, `haskell-sort-imports' and
|
||||
`haskell-align-imports' for operating on module import lines in
|
||||
Haskell source code
|
||||
|
||||
- Add new binding for "C-c C-." to haskell-mode's keymap to sort and
|
||||
realign Haskell module imports
|
||||
|
||||
- Add new binding for "C-c i" to haskell-mode's keymap to jump back and
|
||||
forth from/to the current Haskell module's module import section.
|
||||
|
||||
- New `inferior-haskell-kind' function for querying kind via GHCi's ":kind"
|
||||
|
||||
- New `inferior-haskell-send-decl' for sending declarations to GHCi
|
||||
(bound to "C-x C-d" by default)
|
||||
|
||||
- Add new `haskell-doc-use-inf-haskell` customization variable
|
||||
|
||||
- Add support for bird-style literate haskell editing and a new
|
||||
related customization variable
|
||||
`haskell-indentation-birdtrack-extra-space'
|
||||
|
||||
- Font locking improvements
|
||||
- Add support for Git's merge annotation
|
||||
(with `font-lock-preprocessor-face')
|
||||
- Improve `import', `foreign import' and `foreign export' font
|
||||
locking
|
||||
- Add support for `rec', `proc' and `mdo` as keywords
|
||||
- Make whitespace within `-- |' and `{- |' optional when possible
|
||||
|
||||
- New `haskell-move-nested` module providing utilities for
|
||||
interactively {in,de}denting nested "hanging" blocks.
|
||||
|
||||
- Add stylish-haskell support
|
||||
(enable via `haskell-stylish-on-save` customization variable)
|
||||
|
||||
- Add support for generating tags on save
|
||||
(enable via `haskell-tags-on-save' customization variable)
|
||||
|
||||
- Set sensible dabbrev defaults in haskell-mode
|
||||
|
||||
- Added `SCC` pragma insert/delete commands
|
||||
(`haskell-mode-insert-scc-at-point` and `haskell-mode-kill-scc-at-point')
|
||||
|
||||
- New experimental `haskell-mode-contextual-space' command
|
||||
|
||||
- And a couple more cleanups/fixes/improvements...
|
||||
|
||||
* Changes in 2.8.0 (since 2.7.0)
|
||||
|
||||
See also [[https://github.com/haskell/haskell-mode/compare/2_7_0...2_8_0][detailed Git history]].
|
||||
|
||||
- Minimal indentation support for arrow syntax
|
||||
|
||||
- Avoid opening a new inf-haskell window if one is already visible.
|
||||
Windows on other virtual desktops or iconified frames don't count.
|
||||
|
||||
- Force comint-process-echoes to nil
|
||||
|
||||
- Autolaunch haskell-mode for files starting with #!/usr/bin/runghc
|
||||
and similar
|
||||
|
||||
- Added minimal major mode for parsing GHC core files, courtesy of Johan Tibell.
|
||||
There is a corresponding Haskell menu entry.
|
||||
|
||||
- Allow configuration of where-clause indentation; M-x customize-group
|
||||
haskell-indentation.
|
||||
|
||||
* Changes since 2.6.4
|
||||
|
||||
- fill-paragraph (M-q) now only affects comments, and correctly
|
||||
handles Haddock commentary. adaptive-fill-mode is turned off, as it
|
||||
was interfering.
|
||||
|
||||
- Yet more unicode symbols
|
||||
|
||||
- Better support for unicode encoding of haskell source files
|
||||
|
||||
- mdo correctly indented
|
||||
|
||||
- Indentation fixes, fixes to the fixes, and fixes to the fixes to the
|
||||
fixes
|
||||
|
||||
- New command: M-x haskell-check, calls (by default) hlint on the
|
||||
current file. Also bound to C-c C-v.
|
||||
|
||||
You can also use the flymake minor mode with this.
|
||||
|
||||
* Changes since 2.5.1
|
||||
|
||||
- Parser corrections for haskell-indentation and haskell-decl-scan
|
||||
|
||||
- haskell-indentation: Pressing tab in the rightmost position now
|
||||
moves to the leftmost, by default with a warning.
|
||||
|
||||
- Typo fix: One haskell-indentation variable had ended up in the
|
||||
haskell-ntation customize group.
|
||||
|
||||
- haskell-hoogle aliased to hoogle, haskell-hayoo aliased to hayoo
|
||||
|
||||
- Courtesy of Alex Ott:
|
||||
- Additional unicode symbols for font-lock-symbols: () == /= >= <= !! && || sqrt
|
||||
- M-x haskell-hayoo search added, opens using browse-url
|
||||
- Bug-fix for inferior-haskell-type
|
||||
|
||||
- If haskell-indentation errors out, it now fail-safes to inserting
|
||||
a literal newline or deleting one character, for return and
|
||||
backspace respectively.
|
||||
|
||||
* Changes since 2.4:
|
||||
|
||||
- haskell-indentation, a new minor mode for indentation.
|
||||
|
||||
* Changes since 2.3:
|
||||
|
||||
- Update license to GPLv3.
|
||||
|
||||
- New derived major mode for .hsc files.
|
||||
|
||||
- Removed the C-c C-r binding to reload a file. You can still call
|
||||
inferior-haskell-reload-file (and/or bind it to your favorite key,
|
||||
including C-c C-r) or you can now use C-u C-c C-l.
|
||||
|
||||
- C-c C-d looks up the symbol at point in the Haddock docs.
|
||||
|
||||
- Haddock comments are highlighted with font-lock-doc-face if it exists.
|
||||
|
||||
- Use `tex' rather than `latex' for haskell-literate.
|
||||
|
||||
- inf-haskell.el tries to find the root of the module hierarchy to determine
|
||||
the root of a project (either by looking for a Cabal file or relying on
|
||||
the `module' declaration line). If all works well, this will make C-c C-l
|
||||
automatically switch to the root dir, so that dependencies in other
|
||||
directories are automatically found. If it doesn't, complain and/or set
|
||||
inferior-haskell-find-project-root to nil.
|
||||
|
||||
- The new command haskell-hoogle helps you query Hoogle from Emacs.
|
||||
|
||||
* Changes since 2.2:
|
||||
|
||||
- Trivial support for Cabal package description files.
|
||||
|
||||
- Minor bug fixes.
|
||||
|
||||
* Changes since 2.1:
|
||||
|
||||
- There are now commands to find type and info of identifiers by querying an
|
||||
inferior haskell process. Available under C-c C-t, C-c C-i, and C-c M-.
|
||||
|
||||
- Indentation now looks back further, until a line that has no indentation.
|
||||
To recover the earlier behavior of stopping at the first empty line
|
||||
instead, configure haskell-indent-look-past-empty-line.
|
||||
|
||||
- inf-haskell can wait until a file load completes and jump directly to the
|
||||
first error, like haskell-ghci and haskell-hugs used to do. See the var
|
||||
inferior-haskell-wait-and-jump.
|
||||
|
||||
* Changes since 2.0:
|
||||
|
||||
- inf-haskell uses ghci if hugs is absent.
|
||||
|
||||
- Fix up some binding conflicts (C-c C-o in haskell-doc)
|
||||
|
||||
- Many (hopefully minor) changes to the indentation.
|
||||
|
||||
- New symbols in haskell-font-lock-symbols-alist.
|
||||
|
||||
* Changes since 1.45:
|
||||
|
||||
- keybindings C-c <char> have been replaced by C-c C-<char> so as not
|
||||
to collide with minor modes.
|
||||
|
||||
- The following modules are now automatically activated without having to
|
||||
add anything to haskell-mode-hook:
|
||||
haskell-font-lock (just turn on global-font-lock-mode).
|
||||
haskell-decl-scan (just bind `imenu' to some key).
|
||||
|
||||
- In recent Emacsen, haskell-doc hooks into eldoc-mode.
|
||||
|
||||
- haskell-hugs and haskell-ghci are superceded by inf-haskell.
|
||||
|
||||
- Indentation rules have been improved when using layout inside parens/braces.
|
||||
|
||||
- Symbols like -> and \ can be displayed as actual arrows and lambdas.
|
||||
See haskell-font-lock-symbols.
|
||||
|
||||
- Tweaks to the font-lock settings. Among other things paren-matching
|
||||
with things like \(x,y) should work correctly now.
|
||||
|
||||
- New maintainer <monnier@gnu.org>.
|
||||
18
elpa/haskell-mode-20171022.26/dir
Normal file
18
elpa/haskell-mode-20171022.26/dir
Normal file
@@ -0,0 +1,18 @@
|
||||
This is the file .../info/dir, which contains the
|
||||
topmost node of the Info hierarchy, called (dir)Top.
|
||||
The first time you invoke Info you start off looking at this node.
|
||||
|
||||
File: dir, Node: Top This is the top of the INFO tree
|
||||
|
||||
This (the Directory node) gives a menu of major topics.
|
||||
Typing "q" exits, "?" lists all Info commands, "d" returns here,
|
||||
"h" gives a primer for first-timers,
|
||||
"mEmacs<Return>" visits the Emacs manual, etc.
|
||||
|
||||
In Emacs, you can click mouse button 2 on a menu item or cross reference
|
||||
to select it.
|
||||
|
||||
* Menu:
|
||||
|
||||
Emacs
|
||||
* Haskell Mode: (haskell-mode). Haskell Development Environment for Emacs(en)
|
||||
123
elpa/haskell-mode-20171022.26/ghc-core.el
Normal file
123
elpa/haskell-mode-20171022.26/ghc-core.el
Normal file
@@ -0,0 +1,123 @@
|
||||
;;; ghc-core.el --- Syntax highlighting module for GHC Core -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2010 Johan Tibell
|
||||
|
||||
;; Author: Johan Tibell <johan.tibell@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Purpose:
|
||||
;;
|
||||
;; To make it easier to read GHC Core output by providing highlighting
|
||||
;; and removal of commonly ignored annotations.
|
||||
|
||||
;;; Code:
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-font-lock)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup ghc-core nil
|
||||
"Major mode for viewing pretty printed GHC Core output."
|
||||
:link '(custom-manual "(haskell-mode)")
|
||||
:group 'haskell
|
||||
:prefix "ghc-core-")
|
||||
|
||||
(defcustom ghc-core-program
|
||||
"ghc"
|
||||
"Name of the GHC executable (excluding any arguments)."
|
||||
:type 'string
|
||||
:group 'ghc-core)
|
||||
|
||||
(defcustom ghc-core-program-args
|
||||
'("-O2")
|
||||
"Additional options to be passed to GHC when generating core output.
|
||||
GHC (see variable `ghc-core-program') is invoked with the basic
|
||||
command line options \"-ddump-simpl -c <source-file>\"
|
||||
followed by the additional options defined here.
|
||||
|
||||
The following `-ddump-simpl` options might be of interest:
|
||||
|
||||
- `-dsuppress-all'
|
||||
- `-dsuppress-uniques'
|
||||
- `-dsuppress-idinfo'
|
||||
- `-dsuppress-module-prefixes'
|
||||
- `-dsuppress-type-signatures'
|
||||
- `-dsuppress-type-applications'
|
||||
- `-dsuppress-coercions'
|
||||
|
||||
See `M-x manual-entry RET ghc' for more details."
|
||||
:type '(repeat (string :tag "Argument"))
|
||||
:group 'ghc-core)
|
||||
|
||||
(define-obsolete-variable-alias 'ghc-core-create-options 'ghc-core-program-args
|
||||
"haskell-mode 13.7")
|
||||
|
||||
(defun ghc-core-clean-region (start end)
|
||||
"Remove commonly ignored annotations and namespace prefixes
|
||||
in the region between START and END."
|
||||
(interactive "r")
|
||||
(save-restriction
|
||||
(narrow-to-region start end)
|
||||
(goto-char (point-min))
|
||||
(while (search-forward-regexp "GHC\.[^\.]*\." nil t)
|
||||
(replace-match "" nil t))
|
||||
(goto-char (point-min))
|
||||
(while (flush-lines "^ *GblId *$" nil))
|
||||
(goto-char (point-min))
|
||||
(while (flush-lines "^ *LclId *$" nil))
|
||||
(goto-char (point-min))
|
||||
(while (flush-lines (concat "^ *\\[\\(?:Arity [0-9]+\\|NoCafRefs\\|"
|
||||
"Str: DmdType\\|Worker \\)"
|
||||
"\\([^]]*\\n?\\).*\\] *$") nil))
|
||||
(goto-char (point-min))
|
||||
(while (search-forward "Main." nil t) (replace-match "" nil t))))
|
||||
|
||||
(defun ghc-core-clean-buffer ()
|
||||
"Remove commonly ignored annotations and namespace prefixes
|
||||
in the current buffer."
|
||||
(interactive)
|
||||
(ghc-core-clean-region (point-min) (point-max)))
|
||||
|
||||
;;;###autoload
|
||||
(defun ghc-core-create-core ()
|
||||
"Compile and load the current buffer as tidy core."
|
||||
(interactive)
|
||||
(save-buffer)
|
||||
(let* ((core-buffer (generate-new-buffer "ghc-core"))
|
||||
(neh (lambda () (kill-buffer core-buffer))))
|
||||
(add-hook 'next-error-hook neh)
|
||||
(apply #'call-process ghc-core-program nil core-buffer nil
|
||||
"-ddump-simpl" "-c" (buffer-file-name) ghc-core-program-args)
|
||||
(display-buffer core-buffer)
|
||||
(with-current-buffer core-buffer
|
||||
(ghc-core-mode))
|
||||
(remove-hook 'next-error-hook neh)))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.hcr\\'" . ghc-core-mode))
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.dump-simpl\\'" . ghc-core-mode))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode ghc-core-mode haskell-mode "GHC-Core"
|
||||
"Major mode for GHC Core files.")
|
||||
|
||||
(provide 'ghc-core)
|
||||
;;; ghc-core.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/ghc-core.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/ghc-core.elc
Normal file
Binary file not shown.
66
elpa/haskell-mode-20171022.26/ghci-script-mode.el
Normal file
66
elpa/haskell-mode-20171022.26/ghci-script-mode.el
Normal file
@@ -0,0 +1,66 @@
|
||||
;;; ghci-script-mode.el --- GHCi scripts major mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'haskell)
|
||||
|
||||
(defvar ghci-script-mode-keywords
|
||||
;; The comment syntax can't be described simply in syntax-table.
|
||||
;; We could use font-lock-syntactic-keywords, but is it worth it?
|
||||
'(("^[ \t]*--.*" . font-lock-comment-face)
|
||||
("^ *\\([^ \t:]+\\):" (1 font-lock-keyword-face))
|
||||
("^:[a-z{]+ *\\+" . font-lock-keyword-face)
|
||||
("^:[a-z{]+ " . font-lock-keyword-face)))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode ghci-script-mode text-mode "GHCi-Script"
|
||||
"Major mode for working with .ghci files."
|
||||
(setq-local adaptive-fill-mode nil)
|
||||
(setq-local comment-start "-- ")
|
||||
(setq-local comment-padding 0)
|
||||
(setq-local comment-start-skip "[-{]-[ \t]*")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-end-skip "[ \t]*\\(-}\\|\\s>\\)")
|
||||
(setq-local font-lock-defaults '(ghci-script-mode-keywords t t nil nil))
|
||||
(setq-local indent-tabs-mode nil)
|
||||
(setq-local tab-width 8)
|
||||
(when (boundp 'electric-indent-inhibit)
|
||||
(setq electric-indent-inhibit t))
|
||||
(setq-local dabbrev-case-fold-search nil)
|
||||
(setq-local dabbrev-case-distinction nil)
|
||||
(setq-local dabbrev-case-replace nil)
|
||||
(setq-local dabbrev-abbrev-char-regexp "\\sw\\|[.]")
|
||||
(setq haskell-literate nil))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.ghci\\'" . ghci-script-mode))
|
||||
|
||||
(define-key ghci-script-mode-map (kbd "C-c C-l") 'ghci-script-mode-load)
|
||||
|
||||
(defun ghci-script-mode-load ()
|
||||
"Load the current script file into the GHCi session."
|
||||
(interactive)
|
||||
(let ((buffer (haskell-session-interactive-buffer (haskell-session)))
|
||||
(filename (buffer-file-name)))
|
||||
(save-buffer)
|
||||
(with-current-buffer buffer
|
||||
(set-marker haskell-interactive-mode-prompt-start (point-max))
|
||||
(haskell-interactive-mode-run-expr
|
||||
(concat ":script " filename)))))
|
||||
|
||||
(provide 'ghci-script-mode)
|
||||
BIN
elpa/haskell-mode-20171022.26/ghci-script-mode.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/ghci-script-mode.elc
Normal file
Binary file not shown.
231
elpa/haskell-mode-20171022.26/haskell-align-imports.el
Normal file
231
elpa/haskell-mode-20171022.26/haskell-align-imports.el
Normal file
@@ -0,0 +1,231 @@
|
||||
;;; haskell-align-imports.el --- Align the import lines in a Haskell file -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2010 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This program is free software: you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation, either version 3 of
|
||||
;; the License, or (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be
|
||||
;; useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
;; PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public
|
||||
;; License along with this program. If not, see
|
||||
;; <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Consider the following imports list:
|
||||
;;
|
||||
;; import One
|
||||
;; import Two as A
|
||||
;; import qualified Three
|
||||
;; import qualified Four as PRELUDE
|
||||
;; import Five (A)
|
||||
;; import Six (A,B)
|
||||
;; import qualified Seven (A,B)
|
||||
;; import "abc" Eight
|
||||
;; import "abc" Nine as TWO
|
||||
;; import qualified "abc" Ten
|
||||
;; import qualified "defg" Eleven as PRELUDE
|
||||
;; import "barmu" Twelve (A)
|
||||
;; import "zotconpop" Thirteen (A,B)
|
||||
;; import qualified "z" Fourteen (A,B)
|
||||
;; import Fifteen hiding (A)
|
||||
;; import Sixteen as TWO hiding (A)
|
||||
;; import qualified Seventeen hiding (A)
|
||||
;; import qualified Eighteen as PRELUDE hiding (A)
|
||||
;; import "abc" Nineteen hiding (A)
|
||||
;; import "abc" Twenty as TWO hiding (A)
|
||||
;;
|
||||
;; When haskell-align-imports is run within the same buffer, the
|
||||
;; import list is transformed to:
|
||||
;;
|
||||
;; import "abc" Eight
|
||||
;; import qualified Eighteen as PRELUDE hiding (A)
|
||||
;; import qualified "defg" Eleven as PRELUDE
|
||||
;; import Fifteen hiding (A)
|
||||
;; import Five (A)
|
||||
;; import qualified Four as PRELUDE
|
||||
;; import qualified "z" Fourteen (A,B)
|
||||
;; import "abc" Nine as TWO
|
||||
;; import "abc" Nineteen hiding (A)
|
||||
;; import One
|
||||
;; import qualified Seven (A,B)
|
||||
;; import qualified Seventeen hiding (A)
|
||||
;; import Six (A,B)
|
||||
;; import Sixteen as TWO hiding (A)
|
||||
;; import qualified "abc" Ten
|
||||
;; import "zotconpop" Thirteen (A,B)
|
||||
;; import qualified Three
|
||||
;; import "barmu" Twelve (A)
|
||||
;; import "abc" Twenty as TWO hiding (A)
|
||||
;; import Two as A
|
||||
;;
|
||||
;; If you want everything after module names to be padded out, too,
|
||||
;; customize `haskell-align-imports-pad-after-name', and you'll get:
|
||||
;;
|
||||
;; import One
|
||||
;; import Two as A
|
||||
;; import qualified Three
|
||||
;; import qualified Four as PRELUDE
|
||||
;; import Five (A)
|
||||
;; import Six (A,B)
|
||||
;; import qualified Seven (A,B)
|
||||
;; import "abc" Eight
|
||||
;; import "abc" Nine as TWO
|
||||
;; import qualified "abc" Ten
|
||||
;; import qualified "defg" Eleven as PRELUDE
|
||||
;; import "barmu" Twelve (A)
|
||||
;; import "zotconpop" Thirteen (A,B)
|
||||
;; import qualified "z" Fourteen (A,B)
|
||||
;; import Fifteen hiding (A)
|
||||
;; import Sixteen as TWO hiding (A)
|
||||
;; import qualified Seventeen hiding (A)
|
||||
;; import qualified Eighteen as PRELUDE hiding (A)
|
||||
;; import "abc" Nineteen hiding (A)
|
||||
;; import "abc" Twenty as TWO hiding (A)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar haskell-align-imports-regexp
|
||||
(concat "^\\(import[ ]+\\)"
|
||||
"\\(qualified \\)?"
|
||||
"[ ]*\\(\"[^\"]*\" \\)?"
|
||||
"[ ]*\\([A-Za-z0-9_.']+\\)"
|
||||
"[ ]*\\([ ]*as [A-Z][^ ]*\\)?"
|
||||
"[ ]*\\((.*)\\)?"
|
||||
"\\([ ]*hiding (.*)\\)?"
|
||||
"\\( -- .*\\)?[ ]*$")
|
||||
"Regex used for matching components of an import.")
|
||||
|
||||
(defcustom haskell-align-imports-pad-after-name
|
||||
nil
|
||||
"Pad layout after the module name also."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-align-imports ()
|
||||
"Align all the imports in the buffer."
|
||||
(interactive)
|
||||
(when (haskell-align-imports-line-match)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let* ((imports (haskell-align-imports-collect))
|
||||
(padding (haskell-align-imports-padding imports)))
|
||||
(mapc (lambda (x)
|
||||
(goto-char (cdr x))
|
||||
(delete-region (point) (line-end-position))
|
||||
(insert (haskell-align-imports-chomp
|
||||
(haskell-align-imports-fill padding (car x)))))
|
||||
imports))))
|
||||
nil)
|
||||
|
||||
(defun haskell-align-imports-line-match ()
|
||||
"Try to match the current line as a regexp."
|
||||
(let ((line (buffer-substring-no-properties (line-beginning-position)
|
||||
(line-end-position))))
|
||||
(if (string-match "^import " line)
|
||||
line
|
||||
nil)))
|
||||
|
||||
(defun haskell-align-imports-collect ()
|
||||
"Collect a list of mark / import statement pairs."
|
||||
(let ((imports '()))
|
||||
(while (not (or (equal (point) (point-max)) (haskell-align-imports-after-imports-p)))
|
||||
(let ((line (haskell-align-imports-line-match-it)))
|
||||
(when line
|
||||
(let ((match
|
||||
(haskell-align-imports-merge-parts
|
||||
(cl-loop for i from 1 to 8
|
||||
collect (haskell-align-imports-chomp (match-string i line))))))
|
||||
(setq imports (cons (cons match (line-beginning-position))
|
||||
imports)))))
|
||||
(forward-line))
|
||||
imports))
|
||||
|
||||
(defun haskell-align-imports-merge-parts (l)
|
||||
"Merge together parts of an import statement that shouldn't be separated."
|
||||
(let ((parts (apply #'vector l))
|
||||
(join (lambda (ls)
|
||||
(cl-reduce (lambda (a b)
|
||||
(concat a
|
||||
(if (and (> (length a) 0)
|
||||
(> (length b) 0))
|
||||
" "
|
||||
"")
|
||||
b))
|
||||
ls))))
|
||||
(if haskell-align-imports-pad-after-name
|
||||
(list (funcall join (list (aref parts 0)
|
||||
(aref parts 1)
|
||||
(aref parts 2)))
|
||||
(aref parts 3)
|
||||
(funcall join (list (aref parts 4)
|
||||
(aref parts 5)
|
||||
(aref parts 6)))
|
||||
(aref parts 7))
|
||||
(list (funcall join (list (aref parts 0)
|
||||
(aref parts 1)
|
||||
(aref parts 2)))
|
||||
(funcall join (list (aref parts 3)
|
||||
(aref parts 4)
|
||||
(aref parts 5)
|
||||
(aref parts 6)
|
||||
(aref parts 7)))))))
|
||||
|
||||
(defun haskell-align-imports-chomp (str)
|
||||
"Chomp leading and tailing whitespace from STR."
|
||||
(if str
|
||||
(replace-regexp-in-string "\\(^[[:space:]\n]*\\|[[:space:]\n]*$\\)" ""
|
||||
str)
|
||||
""))
|
||||
|
||||
(defun haskell-align-imports-padding (imports)
|
||||
"Find the padding for each part of the import statements."
|
||||
(if (null imports)
|
||||
imports
|
||||
(cl-reduce (lambda (a b) (cl-mapcar #'max a b))
|
||||
(mapcar (lambda (x) (mapcar #'length (car x)))
|
||||
imports))))
|
||||
|
||||
(defun haskell-align-imports-fill (padding line)
|
||||
"Fill an import line using the padding worked out from all statements."
|
||||
(mapconcat #'identity
|
||||
(cl-mapcar (lambda (pad part)
|
||||
(if (> (length part) 0)
|
||||
(concat part (make-string (- pad (length part)) ? ))
|
||||
(make-string pad ? )))
|
||||
padding
|
||||
line)
|
||||
" "))
|
||||
|
||||
(defun haskell-align-imports-line-match-it ()
|
||||
"Try to match the current line as a regexp."
|
||||
(let ((line (buffer-substring-no-properties (line-beginning-position)
|
||||
(line-end-position))))
|
||||
(if (string-match haskell-align-imports-regexp line)
|
||||
line
|
||||
nil)))
|
||||
|
||||
(defun haskell-align-imports-after-imports-p ()
|
||||
"Are we after the imports list?"
|
||||
(save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(let ((case-fold-search nil))
|
||||
(not (not (search-forward-regexp "\\( = \\|\\<instance\\>\\| :: \\| ∷ \\)"
|
||||
(line-end-position) t 1))))))
|
||||
|
||||
(provide 'haskell-align-imports)
|
||||
|
||||
;;; haskell-align-imports.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-align-imports.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-align-imports.elc
Normal file
Binary file not shown.
207
elpa/haskell-mode-20171022.26/haskell-c2hs.el
Normal file
207
elpa/haskell-mode-20171022.26/haskell-c2hs.el
Normal file
@@ -0,0 +1,207 @@
|
||||
;; haskell-c2hs.el --- -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2016 Sergey Vinokurov
|
||||
;;
|
||||
;; Author: Sergey Vinokurov <serg.foo@gmail.com>
|
||||
;; Created: Monday, 7 March 2016
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;; This mode is mostly intended for highlighting {#...#} hooks.
|
||||
;;
|
||||
;; Quick setup:
|
||||
;; (autoload 'haskell-c2hs-mode "haskell-c2hs-mode" nil t)
|
||||
;; (add-to-list 'auto-mode-alist '("\\.chs\\'" . haskell-c2hs-mode))
|
||||
;;
|
||||
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-font-lock)
|
||||
(require 'haskell-utils)
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.chs\\'" . haskell-c2hs-mode))
|
||||
|
||||
(defface haskell-c2hs-hook-pair-face
|
||||
'((t (:inherit 'font-lock-preprocessor-face)))
|
||||
"Face for highlighting {#...#} pairs."
|
||||
:group 'haskell)
|
||||
|
||||
(defface haskell-c2hs-hook-name-face
|
||||
'((t (:inherit 'font-lock-keyword-face)))
|
||||
"Face for highlighting c2hs hook names."
|
||||
:group 'haskell)
|
||||
|
||||
(defvar haskell-c2hs-font-lock-keywords
|
||||
`((,(eval-when-compile
|
||||
(let* ((ws '(any ?\s ?\t ?\n ?\r))
|
||||
(anychar '(or (not (any ?#))
|
||||
(seq "#"
|
||||
(not (any ?\})))))
|
||||
(any-nonquote '(or (not (any ?# ?\"))
|
||||
(seq "#"
|
||||
(not (any ?\} ?\")))))
|
||||
(cid '(seq (any (?a . ?z) (?A . ?Z) ?_)
|
||||
(* (any (?a . ?z) (?A . ?Z) (?0 . ?9) ?_))))
|
||||
(hsid-type '(seq (? "'")
|
||||
(any (?A . ?Z))
|
||||
(* (any (?a . ?z) (?A . ?Z) (?0 . ?9) ?_ ?'))))
|
||||
(equals-str-val `(seq (* ,ws)
|
||||
"="
|
||||
(* ,ws)
|
||||
"\""
|
||||
(* ,any-nonquote)
|
||||
"\"")))
|
||||
(eval
|
||||
`(rx
|
||||
(seq
|
||||
(group-n 1 "{#")
|
||||
(* ,ws)
|
||||
(or (seq (group-n 2
|
||||
"import"
|
||||
(opt (+ ,ws)
|
||||
"qualified"))
|
||||
(+ ,ws))
|
||||
(seq (group-n 2
|
||||
"context")
|
||||
(opt (+ ,ws)
|
||||
(group-n 3
|
||||
"lib")
|
||||
,equals-str-val)
|
||||
(opt (+ ,ws)
|
||||
(group-n 4
|
||||
"prefix")
|
||||
,equals-str-val)
|
||||
(opt (+ ,ws)
|
||||
(group-n 5
|
||||
"add"
|
||||
(+ ,ws)
|
||||
"prefix")
|
||||
,equals-str-val))
|
||||
(seq (group-n 2
|
||||
"type")
|
||||
(+ ,ws)
|
||||
,cid)
|
||||
(seq (group-n 2
|
||||
"sizeof")
|
||||
(+ ,ws)
|
||||
,cid)
|
||||
(seq (group-n 2
|
||||
"enum"
|
||||
(+ ,ws)
|
||||
"define")
|
||||
(+ ,ws)
|
||||
,cid)
|
||||
;; TODO: vanilla enum fontification is incomplete
|
||||
(seq (group-n 2
|
||||
"enum")
|
||||
(+ ,ws)
|
||||
,cid
|
||||
(opt (+ ,ws)
|
||||
(group-n 3
|
||||
"as")))
|
||||
;; TODO: fun hook highlighting is incompelete
|
||||
(seq (group-n 2
|
||||
(or "call"
|
||||
"fun")
|
||||
(opt (+ ,ws)
|
||||
"pure")
|
||||
(opt (+ ,ws)
|
||||
"unsafe"))
|
||||
(+ ,ws)
|
||||
,cid
|
||||
(opt (+ ,ws)
|
||||
(group-n 3
|
||||
"as")
|
||||
(opt (+ ,ws)
|
||||
(group-n 8
|
||||
"^"))))
|
||||
(group-n 2
|
||||
"get")
|
||||
(group-n 2
|
||||
"set")
|
||||
(seq (group-n 2
|
||||
"pointer")
|
||||
(or (seq (* ,ws)
|
||||
(group-n 3 "*")
|
||||
(* ,ws))
|
||||
(+ ,ws))
|
||||
,cid
|
||||
(opt (+ ,ws)
|
||||
(group-n 4 "as")
|
||||
(+ ,ws)
|
||||
,hsid-type)
|
||||
(opt (+ ,ws)
|
||||
(group-n 5
|
||||
(or "foreign"
|
||||
"stable")))
|
||||
(opt
|
||||
(or (seq (+ ,ws)
|
||||
(group-n 6
|
||||
"newtype"))
|
||||
(seq (* ,ws)
|
||||
"->"
|
||||
(* ,ws)
|
||||
,hsid-type)))
|
||||
(opt (+ ,ws)
|
||||
(group-n 7
|
||||
"nocode")))
|
||||
(group-n 2
|
||||
"class")
|
||||
(group-n 2
|
||||
"alignof")
|
||||
(group-n 2
|
||||
"offsetof")
|
||||
(seq (group-n 2
|
||||
"const")
|
||||
(+ ,ws)
|
||||
,cid)
|
||||
(seq (group-n 2
|
||||
"typedef")
|
||||
(+ ,ws)
|
||||
,cid
|
||||
(+ ,ws)
|
||||
,hsid-type)
|
||||
(group-n 2
|
||||
"nonGNU")
|
||||
;; TODO: default hook not implemented
|
||||
)
|
||||
(* ,anychar)
|
||||
(group-n 9 "#}"))))))
|
||||
;; Override highlighting for pairs in order to always distinguish them.
|
||||
(1 'haskell-c2hs-hook-pair-face t)
|
||||
(2 'haskell-c2hs-hook-name-face)
|
||||
;; Make matches lax, i.e. do not signal error if nothing
|
||||
;; matched.
|
||||
(3 'haskell-c2hs-hook-name-face nil t)
|
||||
(4 'haskell-c2hs-hook-name-face nil t)
|
||||
(5 'haskell-c2hs-hook-name-face nil t)
|
||||
(6 'haskell-c2hs-hook-name-face nil t)
|
||||
(7 'haskell-c2hs-hook-name-face nil t)
|
||||
(8 'font-lock-negation-char-face nil t)
|
||||
;; Override highlighting for pairs in order to always distinguish them.
|
||||
(9 'haskell-c2hs-hook-pair-face t))
|
||||
,@(haskell-font-lock-keywords)))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode haskell-c2hs-mode haskell-mode "C2HS"
|
||||
"Mode for editing *.chs files of the c2hs haskell tool."
|
||||
(setq-local font-lock-defaults
|
||||
(cons 'haskell-c2hs-font-lock-keywords
|
||||
(cdr font-lock-defaults))))
|
||||
|
||||
|
||||
(provide 'haskell-c2hs)
|
||||
|
||||
;; haskell-c2hs.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-c2hs.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-c2hs.elc
Normal file
Binary file not shown.
1164
elpa/haskell-mode-20171022.26/haskell-cabal.el
Normal file
1164
elpa/haskell-mode-20171022.26/haskell-cabal.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-cabal.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-cabal.elc
Normal file
Binary file not shown.
114
elpa/haskell-mode-20171022.26/haskell-collapse.el
Normal file
114
elpa/haskell-mode-20171022.26/haskell-collapse.el
Normal file
@@ -0,0 +1,114 @@
|
||||
;;; haskell-collapse.el --- Collapse expressions -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
;; Copyright (c) 2017 Vasantha Ganesh Kanniappan <vasanthaganesh.k@tuta.io>.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'hideshow)
|
||||
|
||||
;;; TODO:
|
||||
;;; -> Make it work for braces
|
||||
|
||||
(defun haskell-hide-toggle ()
|
||||
"Toggle visibility of existing forms at point. "
|
||||
(interactive)
|
||||
(hs-minor-mode 1)
|
||||
(save-excursion
|
||||
(let* ((modified (buffer-modified-p))
|
||||
(inhibit-read-only t)
|
||||
(position (haskell-indented-block))
|
||||
(beg (car position))
|
||||
(end (cdr position)))
|
||||
(if (and beg end)
|
||||
(if (overlays-in beg end)
|
||||
(hs-discard-overlays beg end)
|
||||
(hs-make-overlay beg end 'code)))
|
||||
(set-buffer-modified-p modified))))
|
||||
|
||||
(defun haskell-blank-line-p ()
|
||||
"Returns `t' if line is empty or composed only of whitespace."
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(= (point-at-eol)
|
||||
(progn (skip-chars-forward "[:blank:]") (point)))))
|
||||
|
||||
(defun haskell-indented-block ()
|
||||
"return (start-of-indentation . end-of-indentation)"
|
||||
(let ((cur-indent (current-indentation))
|
||||
(nxt-line-indent (haskell-next-line-indentation 1))
|
||||
(prev-line-indent (haskell-next-line-indentation -1))
|
||||
(beg-of-line (save-excursion (end-of-line)
|
||||
(point))))
|
||||
(cond ((and (= cur-indent 0)
|
||||
(= nxt-line-indent 0)) nil)
|
||||
((haskell-blank-line-p) nil)
|
||||
((> nxt-line-indent cur-indent)
|
||||
(cons beg-of-line
|
||||
(haskell-find-line-with-indentation '> 1)))
|
||||
((or (= nxt-line-indent cur-indent)
|
||||
(<= prev-line-indent cur-indent))
|
||||
(cons (haskell-find-line-with-indentation '>= -1)
|
||||
(haskell-find-line-with-indentation '>= 1)))
|
||||
(t nil))))
|
||||
|
||||
(defun haskell-next-line-indentation (dir)
|
||||
"returns (integer) indentation of the next if dir=1, previous line
|
||||
indentation if dir=-1"
|
||||
(save-excursion
|
||||
(progn
|
||||
(while (and (zerop (forward-line dir))
|
||||
(haskell-blank-line-p)))
|
||||
(current-indentation))))
|
||||
|
||||
(defun haskell-find-line-with-indentation (comparison direction)
|
||||
"comparison is >= or >, direction if 1 finds forward, if -1 finds backward"
|
||||
(save-excursion
|
||||
(let ((start-indent (current-indentation)))
|
||||
(progn
|
||||
(while (and (zerop (forward-line direction))
|
||||
(or (haskell-blank-line-p)
|
||||
(funcall comparison (current-indentation) start-indent))))
|
||||
(when (= direction 1) (forward-line -1))
|
||||
(end-of-line)
|
||||
(point)))))
|
||||
|
||||
(defun haskell-hide-toggle-all ()
|
||||
"hides all top level functions"
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(while (zerop (forward-line -1))
|
||||
(goto-char (point-at-bol))
|
||||
(when (= (current-indentation) 0) (haskell-hide-toggle)))))
|
||||
|
||||
(defvar haskell-collapse-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "C-c @ C-c") 'haskell-hide-toggle)
|
||||
(define-key map (kbd "C-c @ C-M-c") 'haskell-hide-toggle-all)
|
||||
(define-key map (kbd "C-c @ C-M-s") 'haskell-hide-toggle-all)
|
||||
(define-key map (kbd "C-c @ C-M-h") 'haskell-hide-toggle-all)
|
||||
map)
|
||||
"Keymap for using `haskell-collapse-mode'.")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode haskell-collapse-mode
|
||||
"Minor mode to collapse and expand haskell expressions"
|
||||
:init-value nil
|
||||
:lighter " Haskell-Collapse"
|
||||
:keymap haskell-collapse-mode-map)
|
||||
|
||||
(provide 'haskell-collapse)
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-collapse.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-collapse.elc
Normal file
Binary file not shown.
957
elpa/haskell-mode-20171022.26/haskell-commands.el
Normal file
957
elpa/haskell-mode-20171022.26/haskell-commands.el
Normal file
@@ -0,0 +1,957 @@
|
||||
;;; haskell-commands.el --- Commands that can be run on the process -*- lexical-binding: t -*-
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; This module provides varoius `haskell-mode' and `haskell-interactive-mode'
|
||||
;;; specific commands such as show type signature, show info, haskell process
|
||||
;;; commands and etc.
|
||||
|
||||
;; Copyright © 2014 Chris Done. All rights reserved.
|
||||
;; 2016 Arthur Fayzrakhmanov
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'etags)
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-compat)
|
||||
(require 'haskell-process)
|
||||
(require 'haskell-font-lock)
|
||||
(require 'haskell-interactive-mode)
|
||||
(require 'haskell-session)
|
||||
(require 'haskell-string)
|
||||
(require 'haskell-presentation-mode)
|
||||
(require 'haskell-utils)
|
||||
(require 'highlight-uses-mode)
|
||||
(require 'haskell-cabal)
|
||||
|
||||
(defcustom haskell-mode-stylish-haskell-path "stylish-haskell"
|
||||
"Path to `stylish-haskell' executable."
|
||||
:group 'haskell
|
||||
:type 'string)
|
||||
|
||||
(defcustom haskell-interactive-set-+c
|
||||
t
|
||||
"Issue ':set +c' in interactive session to support type introspection."
|
||||
:group 'haskell-interactive
|
||||
:type 'boolean)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-restart ()
|
||||
"Restart the inferior Haskell process."
|
||||
(interactive)
|
||||
(haskell-process-reset (haskell-interactive-process))
|
||||
(haskell-process-set (haskell-interactive-process) 'command-queue nil)
|
||||
(haskell-process-start (haskell-interactive-session)))
|
||||
|
||||
(defun haskell-process-start (session)
|
||||
"Start the inferior Haskell process with a given SESSION.
|
||||
You can create new session using function `haskell-session-make'."
|
||||
(let ((existing-process (get-process (haskell-session-name (haskell-interactive-session)))))
|
||||
(when (processp existing-process)
|
||||
(haskell-interactive-mode-echo session "Restarting process ...")
|
||||
(haskell-process-set (haskell-session-process session) 'is-restarting t)
|
||||
(delete-process existing-process)))
|
||||
(let ((process (or (haskell-session-process session)
|
||||
(haskell-process-make (haskell-session-name session))))
|
||||
(old-queue (haskell-process-get (haskell-session-process session)
|
||||
'command-queue)))
|
||||
(haskell-session-set-process session process)
|
||||
(haskell-process-set-session process session)
|
||||
(haskell-process-set-cmd process nil)
|
||||
(haskell-process-set (haskell-session-process session) 'is-restarting nil)
|
||||
(let ((default-directory (haskell-session-cabal-dir session))
|
||||
(log-and-command (haskell-process-compute-process-log-and-command session (haskell-process-type))))
|
||||
(haskell-session-prompt-set-current-dir session (not haskell-process-load-or-reload-prompt))
|
||||
(haskell-process-set-process
|
||||
process
|
||||
(progn
|
||||
(haskell-process-log (propertize (format "%S" log-and-command)))
|
||||
(apply #'start-process (cdr log-and-command)))))
|
||||
(progn (set-process-sentinel (haskell-process-process process) 'haskell-process-sentinel)
|
||||
(set-process-filter (haskell-process-process process) 'haskell-process-filter))
|
||||
(haskell-process-send-startup process)
|
||||
(unless (or (eq 'cabal-repl (haskell-process-type))
|
||||
(eq 'cabal-new-repl (haskell-process-type))
|
||||
(eq 'stack-ghci (haskell-process-type))) ;; Both "cabal repl" and "stack ghci" set the proper CWD.
|
||||
(haskell-process-change-dir session
|
||||
process
|
||||
(haskell-session-current-dir session)))
|
||||
(haskell-process-set process 'command-queue
|
||||
(append (haskell-process-get (haskell-session-process session)
|
||||
'command-queue)
|
||||
old-queue))
|
||||
process))
|
||||
|
||||
(defun haskell-process-send-startup (process)
|
||||
"Send the necessary start messages to haskell PROCESS."
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state process
|
||||
|
||||
:go (lambda (process)
|
||||
;; We must set the prompt last, so that this command as a
|
||||
;; whole produces only one prompt marker as a response.
|
||||
(haskell-process-send-string process
|
||||
(mapconcat #'identity
|
||||
(append '("Prelude.putStrLn \"\""
|
||||
":set -v1")
|
||||
(when haskell-interactive-set-+c
|
||||
'(":set +c"))) ; :type-at in GHC 8+
|
||||
"\n"))
|
||||
(haskell-process-send-string process ":set prompt \"\\4\"")
|
||||
(haskell-process-send-string process (format ":set prompt2 \"%s\""
|
||||
haskell-interactive-prompt2)))
|
||||
|
||||
:live (lambda (process buffer)
|
||||
(when (haskell-process-consume
|
||||
process
|
||||
"^\*\*\* WARNING: \\(.+\\) is writable by someone else, IGNORING!$")
|
||||
(let ((path (match-string 1 buffer)))
|
||||
(haskell-session-modify
|
||||
(haskell-process-session process)
|
||||
'ignored-files
|
||||
(lambda (files)
|
||||
(cl-remove-duplicates (cons path files) :test 'string=)))
|
||||
(haskell-interactive-mode-compile-warning
|
||||
(haskell-process-session process)
|
||||
(format "GHCi is ignoring: %s (run M-x haskell-process-unignore)"
|
||||
path)))))
|
||||
|
||||
:complete (lambda (process _)
|
||||
(haskell-interactive-mode-echo
|
||||
(haskell-process-session process)
|
||||
(concat (nth (random (length haskell-process-greetings))
|
||||
haskell-process-greetings)
|
||||
(when haskell-process-show-debug-tips
|
||||
"
|
||||
If I break, you can:
|
||||
1. Restart: M-x haskell-process-restart
|
||||
2. Configure logging: C-h v haskell-process-log (useful for debugging)
|
||||
3. General config: M-x customize-mode
|
||||
4. Hide these tips: C-h v haskell-process-show-debug-tips")))
|
||||
(with-current-buffer (haskell-interactive-buffer)
|
||||
(goto-char haskell-interactive-mode-prompt-start))))))
|
||||
|
||||
(defun haskell-commands-process ()
|
||||
"Get the Haskell session, throws an error if not available."
|
||||
(or (haskell-session-process (haskell-session-maybe))
|
||||
(error "No Haskell session/process associated with this
|
||||
buffer. Maybe run M-x haskell-session-change?")))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-clear ()
|
||||
"Clear the current process."
|
||||
(interactive)
|
||||
(haskell-process-reset (haskell-commands-process))
|
||||
(haskell-process-set (haskell-commands-process) 'command-queue nil))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-interrupt ()
|
||||
"Interrupt the process (SIGINT)."
|
||||
(interactive)
|
||||
(interrupt-process (haskell-process-process (haskell-commands-process))))
|
||||
|
||||
(defun haskell-process-reload-with-fbytecode (process module-buffer)
|
||||
"Query a PROCESS to reload MODULE-BUFFER with -fbyte-code set.
|
||||
Restores -fobject-code after reload finished.
|
||||
MODULE-BUFFER is the actual Emacs buffer of the module being loaded."
|
||||
(haskell-process-queue-without-filters process ":set -fbyte-code")
|
||||
;; We prefix the module's filename with a "*", which asks ghci to
|
||||
;; ignore any existing object file and interpret the module.
|
||||
;; Dependencies will still use their object files as usual.
|
||||
(haskell-process-queue-without-filters
|
||||
process
|
||||
(format ":load \"*%s\""
|
||||
(replace-regexp-in-string
|
||||
"\""
|
||||
"\\\\\""
|
||||
(buffer-file-name module-buffer))))
|
||||
(haskell-process-queue-without-filters process ":set -fobject-code"))
|
||||
|
||||
(defvar url-http-response-status)
|
||||
(defvar url-http-end-of-headers)
|
||||
(defvar haskell-cabal-targets-history nil
|
||||
"History list for session targets.")
|
||||
|
||||
(defun haskell-process-hayoo-ident (ident)
|
||||
"Hayoo for IDENT, return a list of modules"
|
||||
;; We need a real/simulated closure, because otherwise these
|
||||
;; variables will be unbound when the url-retrieve callback is
|
||||
;; called.
|
||||
;; TODO: Remove when this code is converted to lexical bindings by
|
||||
;; default (Emacs 24.1+)
|
||||
(let ((url (format haskell-process-hayoo-query-url (url-hexify-string ident))))
|
||||
(with-current-buffer (url-retrieve-synchronously url)
|
||||
(if (= 200 url-http-response-status)
|
||||
(progn
|
||||
(goto-char url-http-end-of-headers)
|
||||
(let* ((res (json-read))
|
||||
(results (assoc-default 'result res)))
|
||||
;; TODO: gather packages as well, and when we choose a
|
||||
;; given import, check that we have the package in the
|
||||
;; cabal file as well.
|
||||
(cl-mapcan (lambda (r)
|
||||
;; append converts from vector -> list
|
||||
(append (assoc-default 'resultModules r) nil))
|
||||
results)))
|
||||
(warn "HTTP error %s fetching %s" url-http-response-status url)))))
|
||||
|
||||
(defun haskell-process-hoogle-ident (ident)
|
||||
"Hoogle for IDENT, return a list of modules."
|
||||
(with-temp-buffer
|
||||
(let ((hoogle-error (call-process "hoogle" nil t nil "search" "--exact" ident)))
|
||||
(goto-char (point-min))
|
||||
(unless (or (/= 0 hoogle-error)
|
||||
(looking-at "^No results found")
|
||||
(looking-at "^package "))
|
||||
(while (re-search-forward "^\\([^ ]+\\).*$" nil t)
|
||||
(replace-match "\\1" nil nil))
|
||||
(cl-remove-if (lambda (a) (string= "" a))
|
||||
(split-string (buffer-string)
|
||||
"\n"))))))
|
||||
|
||||
(defun haskell-process-haskell-docs-ident (ident)
|
||||
"Search with haskell-docs for IDENT, return a list of modules."
|
||||
(cl-remove-if-not
|
||||
(lambda (a) (string-match "^[[:upper:]][[:alnum:]_'.]+$" a))
|
||||
(split-string
|
||||
(with-output-to-string
|
||||
(with-current-buffer
|
||||
standard-output
|
||||
(call-process "haskell-docs"
|
||||
nil ; no infile
|
||||
t ; output to current buffer (that is string)
|
||||
nil ; do not redisplay
|
||||
"--modules" ident)))
|
||||
"\n")))
|
||||
|
||||
(defun haskell-process-import-modules (process modules)
|
||||
"Query PROCESS `:m +' command to import MODULES."
|
||||
(when haskell-process-auto-import-loaded-modules
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (cons process modules)
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string
|
||||
(car state)
|
||||
(format ":m + %s" (mapconcat 'identity (cdr state) " "))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-describe (ident)
|
||||
"Describe the given identifier IDENT."
|
||||
(interactive (list (read-from-minibuffer "Describe identifier: "
|
||||
(haskell-ident-at-point))))
|
||||
(let ((results (read (shell-command-to-string
|
||||
(concat "haskell-docs --sexp "
|
||||
ident)))))
|
||||
(help-setup-xref (list #'haskell-describe ident)
|
||||
(called-interactively-p 'interactive))
|
||||
(save-excursion
|
||||
(with-help-window (help-buffer)
|
||||
(with-current-buffer (help-buffer)
|
||||
(if results
|
||||
(cl-loop for result in results
|
||||
do (insert (propertize ident 'font-lock-face
|
||||
'((:inherit font-lock-type-face
|
||||
:underline t)))
|
||||
" is defined in "
|
||||
(let ((module (cadr (assoc 'module result))))
|
||||
(if module
|
||||
(concat module " ")
|
||||
""))
|
||||
(cadr (assoc 'package result))
|
||||
"\n\n")
|
||||
do (let ((type (cadr (assoc 'type result))))
|
||||
(when type
|
||||
(insert (haskell-fontify-as-mode type 'haskell-mode)
|
||||
"\n")))
|
||||
do (let ((args (cadr (assoc 'type results))))
|
||||
(cl-loop for arg in args
|
||||
do (insert arg "\n"))
|
||||
(insert "\n"))
|
||||
do (insert (cadr (assoc 'documentation result)))
|
||||
do (insert "\n\n"))
|
||||
(insert "No results for " ident)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-rgrep (&optional prompt)
|
||||
"Grep the effective project for the symbol at point.
|
||||
Very useful for codebase navigation.
|
||||
|
||||
Prompts for an arbitrary regexp given a prefix arg PROMPT."
|
||||
(interactive "P")
|
||||
(let ((sym (if prompt
|
||||
(read-from-minibuffer "Look for: ")
|
||||
(haskell-ident-at-point))))
|
||||
(rgrep sym
|
||||
"*.hs *.lhs *.hsc *.chs *.hs-boot *.lhs-boot"
|
||||
(haskell-session-current-dir (haskell-interactive-session)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-do-info (&optional prompt-value)
|
||||
"Print info on the identifier at point.
|
||||
If PROMPT-VALUE is non-nil, request identifier via mini-buffer."
|
||||
(interactive "P")
|
||||
(let ((at-point (haskell-ident-at-point)))
|
||||
(when (or prompt-value at-point)
|
||||
(let* ((ident (replace-regexp-in-string
|
||||
"^!\\([A-Z_a-z]\\)"
|
||||
"\\1"
|
||||
(if prompt-value
|
||||
(read-from-minibuffer "Info: " at-point)
|
||||
at-point)))
|
||||
(modname (unless prompt-value
|
||||
(haskell-utils-parse-import-statement-at-point)))
|
||||
(command (cond
|
||||
(modname
|
||||
(format ":browse! %s" modname))
|
||||
((string= ident "") ; For the minibuffer input case
|
||||
nil)
|
||||
(t (format (if (string-match "^[a-zA-Z_]" ident)
|
||||
":info %s"
|
||||
":info (%s)")
|
||||
(or ident
|
||||
at-point))))))
|
||||
(when command
|
||||
(haskell-process-show-repl-response command))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-do-type (&optional insert-value)
|
||||
"Print the type of the given expression.
|
||||
|
||||
Given INSERT-VALUE prefix indicates that result type signature
|
||||
should be inserted."
|
||||
(interactive "P")
|
||||
(if insert-value
|
||||
(haskell-process-insert-type)
|
||||
(let* ((expr
|
||||
(if (use-region-p)
|
||||
(buffer-substring-no-properties (region-beginning) (region-end))
|
||||
(haskell-ident-at-point)))
|
||||
(expr-okay (and expr
|
||||
(not (string-match-p "\\`[[:space:]]*\\'" expr))
|
||||
(not (string-match-p "\n" expr)))))
|
||||
;; No newlines in expressions, and surround with parens if it
|
||||
;; might be a slice expression
|
||||
(when expr-okay
|
||||
(haskell-process-show-repl-response
|
||||
(format
|
||||
(if (or (string-match-p "\\`(" expr)
|
||||
(string-match-p "\\`[_[:alpha:]]" expr))
|
||||
":type %s"
|
||||
":type (%s)")
|
||||
expr))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-jump-to-def-or-tag (&optional _next-p)
|
||||
;; FIXME NEXT-P arg is not used
|
||||
"Jump to the definition.
|
||||
Jump to definition of identifier at point by consulting GHCi, or
|
||||
tag table as fallback.
|
||||
|
||||
Remember: If GHCi is busy doing something, this will delay, but
|
||||
it will always be accurate, in contrast to tags, which always
|
||||
work but are not always accurate.
|
||||
If the definition or tag is found, the location from which you jumped
|
||||
will be pushed onto `xref--marker-ring', so you can return to that
|
||||
position with `xref-pop-marker-stack'."
|
||||
(interactive "P")
|
||||
(if (haskell-session-maybe)
|
||||
(let ((initial-loc (point-marker))
|
||||
(loc (haskell-mode-find-def (haskell-ident-at-point))))
|
||||
(haskell-mode-handle-generic-loc loc)
|
||||
(unless (equal initial-loc (point-marker))
|
||||
(xref-push-marker-stack initial-loc)))
|
||||
(call-interactively 'haskell-mode-tag-find)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-goto-loc ()
|
||||
"Go to the location of the thing at point.
|
||||
Requires the :loc-at command from GHCi."
|
||||
(interactive)
|
||||
(let ((loc (haskell-mode-loc-at)))
|
||||
(when loc
|
||||
(haskell-mode-goto-span loc))))
|
||||
|
||||
(defun haskell-mode-goto-span (span)
|
||||
"Jump to the SPAN, whatever file and line and column it needs to get there."
|
||||
(xref-push-marker-stack)
|
||||
(find-file (expand-file-name (plist-get span :path)
|
||||
(haskell-session-cabal-dir (haskell-interactive-session))))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :start-line)))
|
||||
(forward-char (plist-get span :start-col)))
|
||||
|
||||
(defun haskell-process-insert-type ()
|
||||
"Get the identifier at the point and insert its type.
|
||||
Use GHCi's :type if it's possible."
|
||||
(let ((ident (haskell-ident-at-point)))
|
||||
(when ident
|
||||
(let ((process (haskell-interactive-process))
|
||||
(query (format (if (string-match "^[_[:lower:][:upper:]]" ident)
|
||||
":type %s"
|
||||
":type (%s)")
|
||||
ident)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list process query (current-buffer))
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string (nth 0 state)
|
||||
(nth 1 state)))
|
||||
:complete (lambda (state response)
|
||||
(cond
|
||||
;; TODO: Generalize this into a function.
|
||||
((or (string-match "^Top level" response)
|
||||
(string-match "^<interactive>" response))
|
||||
(message "%s" response))
|
||||
(t
|
||||
(with-current-buffer (nth 2 state)
|
||||
(goto-char (line-beginning-position))
|
||||
(insert (format "%s\n" (replace-regexp-in-string "\n$" "" response)))))))))))))
|
||||
|
||||
(defun haskell-mode-find-def (ident)
|
||||
;; TODO Check if it possible to exploit `haskell-process-do-info'
|
||||
"Find definition location of identifier IDENT.
|
||||
Uses the GHCi process to find the location. Returns nil if it
|
||||
can't find the identifier or the identifier isn't a string.
|
||||
|
||||
Returns:
|
||||
|
||||
(library <package> <module>)
|
||||
(file <path> <line> <col>)
|
||||
(module <name>)
|
||||
nil"
|
||||
(when (stringp ident)
|
||||
(let ((reply (haskell-process-queue-sync-request
|
||||
(haskell-interactive-process)
|
||||
(format (if (string-match "^[a-zA-Z_]" ident)
|
||||
":info %s"
|
||||
":info (%s)")
|
||||
ident))))
|
||||
(let ((match (string-match "-- Defined \\(at\\|in\\) \\(.+\\)$" reply)))
|
||||
(when match
|
||||
(let ((defined (match-string 2 reply)))
|
||||
(let ((match (string-match "\\(.+?\\):\\([0-9]+\\):\\([0-9]+\\)$" defined)))
|
||||
(cond
|
||||
(match
|
||||
(list 'file
|
||||
(expand-file-name (match-string 1 defined)
|
||||
(haskell-session-current-dir (haskell-interactive-session)))
|
||||
(string-to-number (match-string 2 defined))
|
||||
(string-to-number (match-string 3 defined))))
|
||||
(t
|
||||
(let ((match (string-match "`\\(.+?\\):\\(.+?\\)'$" defined)))
|
||||
(if match
|
||||
(list 'library
|
||||
(match-string 1 defined)
|
||||
(match-string 2 defined))
|
||||
(let ((match (string-match "`\\(.+?\\)'$" defined)))
|
||||
(if match
|
||||
(list 'module
|
||||
(match-string 1 defined)))))))))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-jump-to-def (ident)
|
||||
"Jump to definition of identifier IDENT at point."
|
||||
(interactive
|
||||
(list
|
||||
(haskell-string-drop-qualifier
|
||||
(haskell-ident-at-point))))
|
||||
(let ((loc (haskell-mode-find-def ident)))
|
||||
(when loc
|
||||
(haskell-mode-handle-generic-loc loc))))
|
||||
|
||||
(defun haskell-mode-handle-generic-loc (loc)
|
||||
"Either jump to or echo a generic location LOC.
|
||||
Either a file or a library."
|
||||
(cl-case (car loc)
|
||||
(file (progn
|
||||
(find-file (elt loc 1))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (elt loc 2)))
|
||||
(goto-char (+ (line-beginning-position)
|
||||
(1- (elt loc 3))))))
|
||||
(library (message "Defined in `%s' (%s)."
|
||||
(elt loc 2)
|
||||
(elt loc 1)))
|
||||
(module (message "Defined in `%s'."
|
||||
(elt loc 1)))))
|
||||
|
||||
(defun haskell-mode-loc-at ()
|
||||
"Get the location at point.
|
||||
Requires the :loc-at command from GHCi."
|
||||
(let ((pos (or (when (region-active-p)
|
||||
(cons (region-beginning)
|
||||
(region-end)))
|
||||
(haskell-spanable-pos-at-point)
|
||||
(cons (point)
|
||||
(point)))))
|
||||
(when pos
|
||||
(let ((reply (haskell-process-queue-sync-request
|
||||
(haskell-interactive-process)
|
||||
(save-excursion
|
||||
(format ":loc-at %s %d %d %d %d %s"
|
||||
(buffer-file-name)
|
||||
(progn (goto-char (car pos))
|
||||
(line-number-at-pos))
|
||||
(1+ (current-column)) ;; GHC uses 1-based columns.
|
||||
(progn (goto-char (cdr pos))
|
||||
(line-number-at-pos))
|
||||
(1+ (current-column)) ;; GHC uses 1-based columns.
|
||||
(buffer-substring-no-properties (car pos)
|
||||
(cdr pos)))))))
|
||||
(if reply
|
||||
(if (string-match "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))"
|
||||
reply)
|
||||
(list :path (match-string 1 reply)
|
||||
:start-line (string-to-number (match-string 2 reply))
|
||||
;; ;; GHC uses 1-based columns.
|
||||
:start-col (1- (string-to-number (match-string 3 reply)))
|
||||
:end-line (string-to-number (match-string 4 reply))
|
||||
;; GHC uses 1-based columns.
|
||||
:end-col (1- (string-to-number (match-string 5 reply))))
|
||||
(error (propertize reply 'face 'compilation-error)))
|
||||
(error (propertize "No reply. Is :loc-at supported?"
|
||||
'face 'compilation-error)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-cd (&optional _not-interactive)
|
||||
;; FIXME optional arg is not used
|
||||
"Change directory."
|
||||
(interactive)
|
||||
(let* ((session (haskell-interactive-session))
|
||||
(dir (haskell-session-prompt-set-current-dir session)))
|
||||
(haskell-process-log
|
||||
(propertize (format "Changing directory to %s ...\n" dir)
|
||||
'face font-lock-comment-face))
|
||||
(haskell-process-change-dir session
|
||||
(haskell-interactive-process)
|
||||
dir)))
|
||||
|
||||
(defun haskell-session-buffer-default-dir (session &optional buffer)
|
||||
"Try to deduce a sensible default directory for SESSION and BUFFER,
|
||||
of which the latter defaults to the current buffer."
|
||||
(or (haskell-session-get session 'current-dir)
|
||||
(haskell-session-get session 'cabal-dir)
|
||||
(if (buffer-file-name buffer)
|
||||
(file-name-directory (buffer-file-name buffer))
|
||||
"~/")))
|
||||
|
||||
(defun haskell-session-prompt-set-current-dir (session &optional use-default)
|
||||
"Prompt for the current directory.
|
||||
Return current working directory for SESSION."
|
||||
(let ((default (haskell-session-buffer-default-dir session)))
|
||||
(haskell-session-set-current-dir
|
||||
session
|
||||
(if use-default
|
||||
default
|
||||
(haskell-utils-read-directory-name "Set current directory: " default))))
|
||||
(haskell-session-get session 'current-dir))
|
||||
|
||||
(defun haskell-process-change-dir (session process dir)
|
||||
"Change SESSION's current directory.
|
||||
Query PROCESS to `:cd` to directory DIR."
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list session process dir)
|
||||
:go
|
||||
(lambda (state)
|
||||
(haskell-process-send-string
|
||||
(cadr state) (format ":cd %s" (cl-caddr state))))
|
||||
|
||||
:complete
|
||||
(lambda (state _)
|
||||
(haskell-session-set-current-dir (car state) (cl-caddr state))
|
||||
(haskell-interactive-mode-echo (car state)
|
||||
(format "Changed directory: %s"
|
||||
(cl-caddr state)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-cabal-macros ()
|
||||
"Send the cabal macros string."
|
||||
(interactive)
|
||||
(haskell-process-queue-without-filters (haskell-interactive-process)
|
||||
":set -optP-include -optPdist/build/autogen/cabal_macros.h"))
|
||||
|
||||
(defun haskell-process-do-try-info (sym)
|
||||
"Get info of SYM and echo in the minibuffer."
|
||||
(let ((process (haskell-interactive-process)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (cons process sym)
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string
|
||||
(car state)
|
||||
(if (string-match "^[A-Za-z_]" (cdr state))
|
||||
(format ":info %s" (cdr state))
|
||||
(format ":info (%s)" (cdr state)))))
|
||||
:complete (lambda (_state response)
|
||||
(unless (or (string-match "^Top level" response)
|
||||
(string-match "^<interactive>" response))
|
||||
(haskell-mode-message-line response)))))))
|
||||
|
||||
(defun haskell-process-do-try-type (sym)
|
||||
"Get type of SYM and echo in the minibuffer."
|
||||
(let ((process (haskell-interactive-process)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (cons process sym)
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string
|
||||
(car state)
|
||||
(if (string-match "^[A-Za-z_]" (cdr state))
|
||||
(format ":type %s" (cdr state))
|
||||
(format ":type (%s)" (cdr state)))))
|
||||
:complete (lambda (_state response)
|
||||
(unless (or (string-match "^Top level" response)
|
||||
(string-match "^<interactive>" response))
|
||||
(haskell-mode-message-line response)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-show-type-at (&optional insert-value)
|
||||
"Show type of the thing at point or within active region asynchronously.
|
||||
This function requires GHCi 8+ or GHCi-ng.
|
||||
|
||||
\\<haskell-interactive-mode-map>
|
||||
To make this function works sometimes you need to load the file in REPL
|
||||
first using command `haskell-process-load-file' bound to
|
||||
\\[haskell-process-load-file].
|
||||
|
||||
Optional argument INSERT-VALUE indicates that
|
||||
recieved type signature should be inserted (but only if nothing
|
||||
happened since function invocation)."
|
||||
(interactive "P")
|
||||
(let* ((pos (haskell-command-capture-expr-bounds))
|
||||
(req (haskell-utils-compose-type-at-command pos))
|
||||
(process (haskell-interactive-process))
|
||||
(buf (current-buffer))
|
||||
(pos-reg (cons pos (region-active-p))))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list process req buf insert-value pos-reg)
|
||||
:go
|
||||
(lambda (state)
|
||||
(let* ((prc (car state))
|
||||
(req (nth 1 state)))
|
||||
(haskell-utils-async-watch-changes)
|
||||
(haskell-process-send-string prc req)))
|
||||
:complete
|
||||
(lambda (state response)
|
||||
(let* ((init-buffer (nth 2 state))
|
||||
(insert-value (nth 3 state))
|
||||
(pos-reg (nth 4 state))
|
||||
(wrap (cdr pos-reg))
|
||||
(min-pos (caar pos-reg))
|
||||
(max-pos (cdar pos-reg))
|
||||
(sig (haskell-utils-reduce-string response))
|
||||
(res-type (haskell-utils-repl-response-error-status sig)))
|
||||
|
||||
(cl-case res-type
|
||||
;; neither popup presentation buffer
|
||||
;; nor insert response in error case
|
||||
('unknown-command
|
||||
(message "This command requires GHCi 8+ or GHCi-ng. Please read command description for details."))
|
||||
('option-missing
|
||||
(message "Could not infer type signature. You need to load file first. Also :set +c is required, see customization `haskell-interactive-set-+c'. Please read command description for details."))
|
||||
('interactive-error (message "Wrong REPL response: %s" sig))
|
||||
(otherwise
|
||||
(if insert-value
|
||||
;; Only insert type signature and do not present it
|
||||
(if (= (length haskell-utils-async-post-command-flag) 1)
|
||||
(if wrap
|
||||
;; Handle region case
|
||||
(progn
|
||||
(deactivate-mark)
|
||||
(save-excursion
|
||||
(delete-region min-pos max-pos)
|
||||
(goto-char min-pos)
|
||||
(insert (concat "(" sig ")"))))
|
||||
;; Non-region cases
|
||||
(haskell-command-insert-type-signature sig))
|
||||
;; Some commands registered, prevent insertion
|
||||
(message "Type signature insertion was prevented. These commands were registered: %s"
|
||||
(cdr (reverse haskell-utils-async-post-command-flag))))
|
||||
;; Present the result only when response is valid and not asked
|
||||
;; to insert result
|
||||
(haskell-command-echo-or-present response)))
|
||||
|
||||
(haskell-utils-async-stop-watching-changes init-buffer))))))))
|
||||
|
||||
(make-obsolete 'haskell-process-generate-tags
|
||||
'haskell-mode-generate-tags
|
||||
"2016-03-14")
|
||||
(defun haskell-process-generate-tags (&optional and-then-find-this-tag)
|
||||
"Regenerate the TAGS table.
|
||||
If optional AND-THEN-FIND-THIS-TAG argument is present it is used with
|
||||
function `xref-find-definitions' after new table was generated."
|
||||
(interactive)
|
||||
(let ((process (haskell-interactive-process)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (cons process and-then-find-this-tag)
|
||||
:go
|
||||
(lambda (state)
|
||||
(let* ((process (car state))
|
||||
(cabal-dir (haskell-session-cabal-dir
|
||||
(haskell-process-session process)))
|
||||
(command (haskell-cabal--compose-hasktags-command cabal-dir)))
|
||||
(haskell-process-send-string process command)))
|
||||
:complete (lambda (state _response)
|
||||
(when (cdr state)
|
||||
(let ((tags-file-name
|
||||
(haskell-session-tags-filename
|
||||
(haskell-process-session (car state)))))
|
||||
(xref-find-definitions (cdr state))))
|
||||
(haskell-mode-message-line "Tags generated."))))))
|
||||
|
||||
(defun haskell-process-add-cabal-autogen ()
|
||||
"Add cabal's autogen dir to the GHCi search path.
|
||||
Add <cabal-project-dir>/dist/build/autogen/ to GHCi seatch path.
|
||||
This allows modules such as 'Path_...', generated by cabal, to be
|
||||
loaded by GHCi."
|
||||
(unless (or (eq 'cabal-repl (haskell-process-type))
|
||||
(eq 'cabal-new-repl (haskell-process-type))) ;; redundant with "cabal repl"
|
||||
(let*
|
||||
((session (haskell-interactive-session))
|
||||
(cabal-dir (haskell-session-cabal-dir session))
|
||||
(ghci-gen-dir (format "%sdist/build/autogen/" cabal-dir)))
|
||||
(haskell-process-queue-without-filters
|
||||
(haskell-interactive-process)
|
||||
(format ":set -i%s" ghci-gen-dir)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-unignore ()
|
||||
"Unignore any ignored files.
|
||||
Do not ignore files that were specified as being ignored by the
|
||||
inferior GHCi process."
|
||||
(interactive)
|
||||
(let ((session (haskell-interactive-session))
|
||||
(changed nil))
|
||||
(if (null (haskell-session-get session 'ignored-files))
|
||||
(message "Nothing to unignore!")
|
||||
(cl-loop for file in (haskell-session-get session 'ignored-files)
|
||||
do
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(progn
|
||||
(cl-case
|
||||
(read-event
|
||||
(propertize
|
||||
(format "Set permissions? %s (y, n, v: stop and view file)"
|
||||
file)
|
||||
'face
|
||||
'minibuffer-prompt))
|
||||
(?y
|
||||
(haskell-process-unignore-file session file)
|
||||
(setq changed t))
|
||||
(?v
|
||||
(find-file file)
|
||||
(cl-return)))
|
||||
(when (and changed
|
||||
(y-or-n-p "Restart GHCi process now? "))
|
||||
(haskell-process-restart)))
|
||||
;; unwind
|
||||
(haskell-mode-toggle-interactive-prompt-state t))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-change-target (target)
|
||||
"Set the build TARGET for cabal REPL."
|
||||
(interactive
|
||||
(list
|
||||
(completing-read "New build target: "
|
||||
(haskell-cabal-enum-targets (haskell-process-type))
|
||||
nil
|
||||
nil
|
||||
nil
|
||||
'haskell-cabal-targets-history)))
|
||||
(let* ((session haskell-session)
|
||||
(old-target (haskell-session-get session 'target)))
|
||||
(when session
|
||||
(haskell-session-set-target session target)
|
||||
(when (not (string= old-target target))
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(when (y-or-n-p "Target changed, restart haskell process?")
|
||||
(haskell-process-start session)))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-stylish-buffer ()
|
||||
"Apply stylish-haskell to the current buffer.
|
||||
|
||||
Use `haskell-mode-stylish-haskell-path' to know where to find
|
||||
stylish-haskell executable. This function tries to preserve
|
||||
cursor position and markers by using
|
||||
`haskell-mode-buffer-apply-command'."
|
||||
(interactive)
|
||||
(haskell-mode-buffer-apply-command haskell-mode-stylish-haskell-path))
|
||||
|
||||
(defun haskell-mode-buffer-apply-command (cmd)
|
||||
"Execute shell command CMD with current buffer as input and output.
|
||||
Use buffer as input and replace the whole buffer with the
|
||||
output. If CMD fails the buffer remains unchanged."
|
||||
(set-buffer-modified-p t)
|
||||
(let* ((out-file (make-temp-file "stylish-output"))
|
||||
(err-file (make-temp-file "stylish-error")))
|
||||
(unwind-protect
|
||||
(let* ((_errcode
|
||||
(call-process-region (point-min) (point-max) cmd nil
|
||||
`((:file ,out-file) ,err-file)
|
||||
nil))
|
||||
(err-file-empty-p
|
||||
(equal 0 (nth 7 (file-attributes err-file))))
|
||||
(out-file-empty-p
|
||||
(equal 0 (nth 7 (file-attributes out-file)))))
|
||||
(if err-file-empty-p
|
||||
(if out-file-empty-p
|
||||
(message "Error: %s produced no output and no error information, leaving buffer alone" cmd)
|
||||
;; Command successful, insert file with replacement to preserve
|
||||
;; markers.
|
||||
(insert-file-contents out-file nil nil nil t))
|
||||
(progn
|
||||
;; non-null stderr, command must have failed
|
||||
(message "Error: %s ended with errors, leaving buffer alone" cmd)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents err-file)
|
||||
;; use (warning-minimum-level :debug) to see this
|
||||
(display-warning cmd
|
||||
(buffer-substring-no-properties (point-min) (point-max))
|
||||
:debug)))))
|
||||
(ignore-errors
|
||||
(delete-file err-file))
|
||||
(ignore-errors
|
||||
(delete-file out-file)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-find-uses ()
|
||||
"Find use cases of the identifier at point and highlight them all."
|
||||
(interactive)
|
||||
(let ((spans (haskell-mode-uses-at)))
|
||||
(unless (null spans)
|
||||
(highlight-uses-mode 1)
|
||||
(cl-loop for span in spans
|
||||
do (haskell-mode-make-use-highlight span)))))
|
||||
|
||||
(defun haskell-mode-make-use-highlight (span)
|
||||
"Make a highlight overlay at the given SPAN."
|
||||
(save-window-excursion
|
||||
(save-excursion
|
||||
(haskell-mode-goto-span span)
|
||||
(save-excursion
|
||||
(highlight-uses-mode-highlight
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :start-line)))
|
||||
(forward-char (plist-get span :start-col))
|
||||
(point))
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :end-line)))
|
||||
(forward-char (plist-get span :end-col))
|
||||
(point)))))))
|
||||
|
||||
(defun haskell-mode-uses-at ()
|
||||
"Get the locations of use cases for the ident at point.
|
||||
Requires the :uses command from GHCi."
|
||||
(let ((pos (or (when (region-active-p)
|
||||
(cons (region-beginning)
|
||||
(region-end)))
|
||||
(haskell-ident-pos-at-point)
|
||||
(cons (point)
|
||||
(point)))))
|
||||
(when pos
|
||||
(let ((reply (haskell-process-queue-sync-request
|
||||
(haskell-interactive-process)
|
||||
(save-excursion
|
||||
(format ":uses %s %d %d %d %d %s"
|
||||
(buffer-file-name)
|
||||
(progn (goto-char (car pos))
|
||||
(line-number-at-pos))
|
||||
(1+ (current-column)) ;; GHC uses 1-based columns.
|
||||
(progn (goto-char (cdr pos))
|
||||
(line-number-at-pos))
|
||||
(1+ (current-column)) ;; GHC uses 1-based columns.
|
||||
(buffer-substring-no-properties (car pos)
|
||||
(cdr pos)))))))
|
||||
(if reply
|
||||
(let ((lines (split-string reply "\n" t)))
|
||||
(cl-remove-if
|
||||
#'null
|
||||
(mapcar (lambda (line)
|
||||
(if (string-match "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))"
|
||||
line)
|
||||
(list :path (match-string 1 line)
|
||||
:start-line (string-to-number (match-string 2 line))
|
||||
;; ;; GHC uses 1-based columns.
|
||||
:start-col (1- (string-to-number (match-string 3 line)))
|
||||
:end-line (string-to-number (match-string 4 line))
|
||||
;; GHC uses 1-based columns.
|
||||
:end-col (1- (string-to-number (match-string 5 line))))
|
||||
(error (propertize line 'face 'compilation-error))))
|
||||
lines)))
|
||||
(error (propertize "No reply. Is :uses supported?"
|
||||
'face 'compilation-error)))))))
|
||||
|
||||
(defun haskell-command-echo-or-present (msg)
|
||||
"Present message in some manner depending on configuration.
|
||||
If variable `haskell-process-use-presentation-mode' is NIL it will output
|
||||
modified message MSG to echo area."
|
||||
(if haskell-process-use-presentation-mode
|
||||
(let ((session (haskell-process-session (haskell-interactive-process))))
|
||||
(haskell-presentation-present session msg))
|
||||
(let ((m (haskell-utils-reduce-string msg)))
|
||||
(message "%s" m))))
|
||||
|
||||
(defun haskell-command-capture-expr-bounds ()
|
||||
"Capture position bounds of expression at point.
|
||||
If there is an active region then it returns region
|
||||
bounds. Otherwise it uses `haskell-spanable-pos-at-point` to
|
||||
capture identifier bounds. If latter function returns NIL this function
|
||||
will return cons cell where min and max positions both are equal
|
||||
to point."
|
||||
(or (when (region-active-p)
|
||||
(cons (region-beginning)
|
||||
(region-end)))
|
||||
(haskell-spanable-pos-at-point)
|
||||
(cons (point) (point))))
|
||||
|
||||
(defun haskell-command-insert-type-signature (signature)
|
||||
"Insert type signature.
|
||||
In case of active region is present, wrap it by parentheses and
|
||||
append SIGNATURE to original expression. Otherwise tries to
|
||||
carefully insert SIGNATURE above identifier at point. Removes
|
||||
newlines and extra whitespace in signature before insertion."
|
||||
(let* ((ident-pos (or (haskell-ident-pos-at-point)
|
||||
(cons (point) (point))))
|
||||
(min-pos (car ident-pos))
|
||||
(sig (haskell-utils-reduce-string signature)))
|
||||
(save-excursion
|
||||
(goto-char min-pos)
|
||||
(let ((col (current-column)))
|
||||
(insert sig "\n")
|
||||
(indent-to col)))))
|
||||
|
||||
(provide 'haskell-commands)
|
||||
;;; haskell-commands.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-commands.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-commands.elc
Normal file
Binary file not shown.
65
elpa/haskell-mode-20171022.26/haskell-compat.el
Normal file
65
elpa/haskell-mode-20171022.26/haskell-compat.el
Normal file
@@ -0,0 +1,65 @@
|
||||
;;; haskell-compat.el --- legacy/compatibility backports for haskell-mode -*- lexical-binding: t -*-
|
||||
;;
|
||||
;; Filename: haskell-compat.el
|
||||
;; Description: legacy/compatibility backports for haskell-mode
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
(require 'etags)
|
||||
(require 'ring)
|
||||
(require 'outline)
|
||||
(require 'xref nil t)
|
||||
|
||||
(eval-when-compile
|
||||
(setq byte-compile-warnings '(not cl-functions obsolete)))
|
||||
|
||||
|
||||
;; Cross-referencing commands have been replaced since Emacs 25.1.
|
||||
;; These aliases are required to provide backward compatibility.
|
||||
(unless (fboundp 'xref-push-marker-stack)
|
||||
(defalias 'xref-pop-marker-stack 'pop-tag-mark)
|
||||
|
||||
(defun xref-push-marker-stack (&optional m)
|
||||
"Add point M (defaults to `point-marker') to the marker stack."
|
||||
(ring-insert find-tag-marker-ring (or m (point-marker)))))
|
||||
|
||||
(unless (fboundp 'outline-hide-sublevels)
|
||||
(defalias 'outline-hide-sublevels 'hide-sublevels))
|
||||
|
||||
(unless (fboundp 'outline-show-subtree)
|
||||
(defalias 'outline-show-subtree 'show-subtree))
|
||||
|
||||
(unless (fboundp 'outline-hide-sublevels)
|
||||
(defalias 'outline-hide-sublevels 'hide-sublevels))
|
||||
|
||||
(unless (fboundp 'outline-show-subtree)
|
||||
(defalias 'outline-show-subtree 'show-subtree))
|
||||
|
||||
(unless (fboundp 'xref-find-definitions)
|
||||
(defun xref-find-definitions (ident)
|
||||
(let ((next-p (and (boundp 'xref-prompt-for-identifier)
|
||||
xref-prompt-for-identifier)))
|
||||
(find-tag ident next-p))))
|
||||
|
||||
(unless (fboundp 'font-lock-ensure)
|
||||
(defalias 'font-lock-ensure 'font-lock-fontify-buffer))
|
||||
|
||||
(provide 'haskell-compat)
|
||||
|
||||
;;; haskell-compat.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-compat.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-compat.elc
Normal file
Binary file not shown.
154
elpa/haskell-mode-20171022.26/haskell-compile.el
Normal file
154
elpa/haskell-mode-20171022.26/haskell-compile.el
Normal file
@@ -0,0 +1,154 @@
|
||||
;;; haskell-compile.el --- Haskell/GHC compilation sub-mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2013 Herbert Valerio Riedel
|
||||
|
||||
;; Author: Herbert Valerio Riedel <hvr@gnu.org>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Simple GHC-centric compilation sub-mode; see info node
|
||||
;; `(haskell-mode)compilation' for more information
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'compile)
|
||||
(require 'haskell-cabal)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell-compile nil
|
||||
"Settings for Haskell compilation mode"
|
||||
:link '(custom-manual "(haskell-mode)compilation")
|
||||
:group 'haskell)
|
||||
|
||||
(defcustom haskell-compile-cabal-build-command
|
||||
"cd %s && cabal build --ghc-option=-ferror-spans"
|
||||
"Default build command to use for `haskell-cabal-build' when a cabal file is detected.
|
||||
The `%s' placeholder is replaced by the cabal package top folder."
|
||||
:group 'haskell-compile
|
||||
:type 'string)
|
||||
|
||||
(defcustom haskell-compile-cabal-build-alt-command
|
||||
"cd %s && cabal clean -s && cabal build --ghc-option=-ferror-spans"
|
||||
"Alternative build command to use when `haskell-cabal-build' is called with a negative prefix argument.
|
||||
The `%s' placeholder is replaced by the cabal package top folder."
|
||||
:group 'haskell-compile
|
||||
:type 'string)
|
||||
|
||||
(defcustom haskell-compile-command
|
||||
"ghc -Wall -ferror-spans -fforce-recomp -c %s"
|
||||
"Default build command to use for `haskell-cabal-build' when no cabal file is detected.
|
||||
The `%s' placeholder is replaced by the current buffer's filename."
|
||||
:group 'haskell-compile
|
||||
:type 'string)
|
||||
|
||||
(defcustom haskell-compile-ghc-filter-linker-messages
|
||||
t
|
||||
"Filter out unremarkable \"Loading package...\" linker messages during compilation."
|
||||
:group 'haskell-compile
|
||||
:type 'boolean)
|
||||
|
||||
(defconst haskell-compilation-error-regexp-alist
|
||||
`((,(concat
|
||||
"^ *\\(?1:[^\t\r\n]+?\\):"
|
||||
"\\(?:"
|
||||
"\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)\\(?:-\\(?5:[0-9]+\\)\\)?" ;; "121:1" & "12:3-5"
|
||||
"\\|"
|
||||
"(\\(?2:[0-9]+\\),\\(?4:[0-9]+\\))-(\\(?3:[0-9]+\\),\\(?5:[0-9]+\\))" ;; "(289,5)-(291,36)"
|
||||
"\\)"
|
||||
":\\(?6:\n?[ \t]+[Ww]arning:\\)?")
|
||||
1 (2 . 3) (4 . 5) (6 . nil)) ;; error/warning locus
|
||||
|
||||
;; multiple declarations
|
||||
("^ \\(?:Declared at:\\| \\) \\(?1:[^ \t\r\n]+\\):\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)$"
|
||||
1 2 4 0) ;; info locus
|
||||
|
||||
;; this is the weakest pattern as it's subject to line wrapping et al.
|
||||
(" at \\(?1:[^ \t\r\n]+\\):\\(?2:[0-9]+\\):\\(?4:[0-9]+\\)\\(?:-\\(?5:[0-9]+\\)\\)?[)]?$"
|
||||
1 2 (4 . 5) 0)) ;; info locus
|
||||
"Regexps used for matching GHC compile messages.
|
||||
See `compilation-error-regexp-alist' for semantics.")
|
||||
|
||||
(defvar haskell-compilation-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map compilation-mode-map))
|
||||
"Keymap for `haskell-compilation-mode' buffers.
|
||||
This is a child of `compilation-mode-map'.")
|
||||
|
||||
(defun haskell-compilation-filter-hook ()
|
||||
"Local `compilation-filter-hook' for `haskell-compilation-mode'."
|
||||
|
||||
(when haskell-compile-ghc-filter-linker-messages
|
||||
(delete-matching-lines "^ *Loading package [^ \t\r\n]+ [.]+ linking [.]+ done\\.$"
|
||||
(save-excursion (goto-char compilation-filter-start)
|
||||
(line-beginning-position))
|
||||
(point))))
|
||||
|
||||
(define-compilation-mode haskell-compilation-mode "HsCompilation"
|
||||
"Haskell/GHC specific `compilation-mode' derivative.
|
||||
This mode provides support for GHC 7.[46]'s compile
|
||||
messages. Specifically, also the `-ferror-spans` source location
|
||||
format is supported, as well as info-locations within compile
|
||||
messages pointing to additional source locations."
|
||||
(setq-local compilation-error-regexp-alist
|
||||
haskell-compilation-error-regexp-alist)
|
||||
|
||||
(add-hook 'compilation-filter-hook
|
||||
'haskell-compilation-filter-hook nil t)
|
||||
)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-compile (&optional edit-command)
|
||||
"Compile the Haskell program including the current buffer.
|
||||
Tries to locate the next cabal description in current or parent
|
||||
folders via `haskell-cabal-find-dir' and if found, invoke
|
||||
`haskell-compile-cabal-build-command' from the cabal package root
|
||||
folder. If no cabal package could be detected,
|
||||
`haskell-compile-command' is used instead.
|
||||
|
||||
If prefix argument EDIT-COMMAND is non-nil (and not a negative
|
||||
prefix `-'), `haskell-compile' prompts for custom compile
|
||||
command.
|
||||
|
||||
If EDIT-COMMAND contains the negative prefix argument `-',
|
||||
`haskell-compile' calls the alternative command defined in
|
||||
`haskell-compile-cabal-build-alt-command' if a cabal package was
|
||||
detected.
|
||||
|
||||
`haskell-compile' uses `haskell-compilation-mode' which is
|
||||
derived from `compilation-mode'. See Info
|
||||
node `(haskell-mode)compilation' for more details."
|
||||
(interactive "P")
|
||||
(save-some-buffers (not compilation-ask-about-save)
|
||||
compilation-save-buffers-predicate)
|
||||
(let* ((cabdir (haskell-cabal-find-dir))
|
||||
(command1 (if (eq edit-command '-)
|
||||
haskell-compile-cabal-build-alt-command
|
||||
haskell-compile-cabal-build-command))
|
||||
(srcname (buffer-file-name))
|
||||
(command (if cabdir
|
||||
(format command1 cabdir)
|
||||
(if (and srcname (derived-mode-p 'haskell-mode))
|
||||
(format haskell-compile-command srcname)
|
||||
command1))))
|
||||
(when (and edit-command (not (eq edit-command '-)))
|
||||
(setq command (compilation-read-command command)))
|
||||
|
||||
(compilation-start command 'haskell-compilation-mode)))
|
||||
|
||||
(provide 'haskell-compile)
|
||||
;;; haskell-compile.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-compile.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-compile.elc
Normal file
Binary file not shown.
131
elpa/haskell-mode-20171022.26/haskell-complete-module.el
Normal file
131
elpa/haskell-mode-20171022.26/haskell-complete-module.el
Normal file
@@ -0,0 +1,131 @@
|
||||
;;; haskell-complete-module.el --- A fast way to complete Haskell module names -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(defcustom haskell-complete-module-preferred
|
||||
'()
|
||||
"Override ordering of module results by specifying preferred modules."
|
||||
:group 'haskell
|
||||
:type '(repeat string))
|
||||
|
||||
(defcustom haskell-complete-module-max-display
|
||||
10
|
||||
"Maximum items to display in minibuffer."
|
||||
:group 'haskell
|
||||
:type 'number)
|
||||
|
||||
(defun haskell-complete-module-read (prompt candidates)
|
||||
"Interactively auto-complete from a list of candidates."
|
||||
(let ((stack (list))
|
||||
(pattern "")
|
||||
(result nil))
|
||||
(delete-dups candidates)
|
||||
(setq candidates
|
||||
(sort candidates
|
||||
(lambda (a b)
|
||||
(let ((a-mem (member a haskell-complete-module-preferred))
|
||||
(b-mem (member b haskell-complete-module-preferred)))
|
||||
(cond
|
||||
((and a-mem (not b-mem))
|
||||
t)
|
||||
((and b-mem (not a-mem))
|
||||
nil)
|
||||
(t
|
||||
(string< a b)))))))
|
||||
(while (not result)
|
||||
(let ((key
|
||||
(key-description
|
||||
(vector
|
||||
(read-key
|
||||
(concat (propertize prompt 'face 'minibuffer-prompt)
|
||||
(propertize pattern 'face 'font-lock-type-face)
|
||||
"{"
|
||||
(mapconcat #'identity
|
||||
(let* ((i 0))
|
||||
(cl-loop for candidate in candidates
|
||||
while (<= i haskell-complete-module-max-display)
|
||||
do (cl-incf i)
|
||||
collect (cond ((> i haskell-complete-module-max-display)
|
||||
"...")
|
||||
((= i 1)
|
||||
(propertize candidate 'face 'ido-first-match-face))
|
||||
(t candidate))))
|
||||
" | ")
|
||||
"}"))))))
|
||||
(cond
|
||||
((string= key "C-g")
|
||||
(keyboard-quit))
|
||||
((string= key "DEL")
|
||||
(unless (null stack)
|
||||
(setq candidates (pop stack)))
|
||||
(unless (string= "" pattern)
|
||||
(setq pattern (substring pattern 0 -1))))
|
||||
((string= key "RET")
|
||||
(setq result (or (car candidates)
|
||||
pattern)))
|
||||
((string= key "<left>")
|
||||
(setq candidates
|
||||
(append (last candidates)
|
||||
(butlast candidates))))
|
||||
((string= key "<right>")
|
||||
(setq candidates
|
||||
(append (cdr candidates)
|
||||
(list (car candidates)))))
|
||||
(t
|
||||
(when (string-match "[A-Za-z0-9_'.]+" key)
|
||||
(push candidates stack)
|
||||
(setq pattern (concat pattern key))
|
||||
(setq candidates (haskell-complete-module pattern candidates)))))))
|
||||
result))
|
||||
|
||||
(defun haskell-complete-module (pattern candidates)
|
||||
"Filter the CANDIDATES using PATTERN."
|
||||
(let ((case-fold-search t))
|
||||
(cl-loop for candidate in candidates
|
||||
when (haskell-complete-module-match pattern candidate)
|
||||
collect candidate)))
|
||||
|
||||
(defun haskell-complete-module-match (pattern text)
|
||||
"Match PATTERN against TEXT."
|
||||
(string-match (haskell-complete-module-regexp pattern)
|
||||
text))
|
||||
|
||||
(defun haskell-complete-module-regexp (pattern)
|
||||
"Make a regular expression for the given module pattern. Example:
|
||||
|
||||
\"c.m.s\" -> \"^c[^.]*\\.m[^.]*\\.s[^.]*\"
|
||||
|
||||
"
|
||||
(let ((components (mapcar #'haskell-complete-module-component
|
||||
(split-string pattern "\\." t))))
|
||||
(concat "^"
|
||||
(mapconcat #'identity
|
||||
components
|
||||
"\\."))))
|
||||
|
||||
(defun haskell-complete-module-component (component)
|
||||
"Make a regular expression for the given component. Example:
|
||||
|
||||
\"co\" -> \"c[^.]*o[^.]*\"
|
||||
|
||||
"
|
||||
(replace-regexp-in-string "\\(.\\)" "\\1[^.]*" component))
|
||||
|
||||
(provide 'haskell-complete-module)
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-complete-module.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-complete-module.elc
Normal file
Binary file not shown.
392
elpa/haskell-mode-20171022.26/haskell-completions.el
Normal file
392
elpa/haskell-mode-20171022.26/haskell-completions.el
Normal file
@@ -0,0 +1,392 @@
|
||||
;;; haskell-completions.el --- Haskell Completion package -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright © 2015-2016 Athur Fayzrakhmanov. All rights reserved.
|
||||
|
||||
;; This file is part of haskell-mode package.
|
||||
;; You can contact with authors using GitHub issue tracker:
|
||||
;; https://github.com/haskell/haskell-mode/issues
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package provides completions related functionality for
|
||||
;; Haskell Mode such grab completion prefix at point, and etc..
|
||||
|
||||
;; Some description
|
||||
;; ================
|
||||
;;
|
||||
;; For major use function `haskell-completions-grab-prefix' is supposed, and
|
||||
;; other prefix grabbing functions are used internally by it. So, only this
|
||||
;; funciton have prefix minimal length functionality and invokes predicate
|
||||
;; function `haskell-completions-can-grab-prefix'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-process)
|
||||
(require 'haskell-interactive-mode)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell-completions nil
|
||||
"Settings for completions provided by `haskell-mode'"
|
||||
:link '(custom-manual "(haskell-mode)Completion support")
|
||||
:group 'haskell)
|
||||
|
||||
(defcustom haskell-completions-complete-operators
|
||||
t
|
||||
"Should `haskell-completions-sync-repl-completion-at-point' complete operators.
|
||||
|
||||
Note: GHCi prior to version 8.0.1 have bug in `:complete`
|
||||
command: when completing operators it returns a list of all
|
||||
imported identifiers (see Track ticket URL
|
||||
`https://ghc.haskell.org/trac/ghc/ticket/10576'). This leads to
|
||||
significant Emacs slowdown. To aviod slowdown you should set
|
||||
this variable to `nil'."
|
||||
:group 'haskell-completions
|
||||
:type 'boolean)
|
||||
|
||||
(defvar haskell-completions--pragma-names
|
||||
(list "DEPRECATED"
|
||||
"INCLUDE"
|
||||
"INCOHERENT"
|
||||
"INLINABLE"
|
||||
"INLINE"
|
||||
"LANGUAGE"
|
||||
"LINE"
|
||||
"MINIMAL"
|
||||
"NOINLINE"
|
||||
"NOUNPACK"
|
||||
"OPTIONS"
|
||||
"OPTIONS_GHC"
|
||||
"OVERLAPPABLE"
|
||||
"OVERLAPPING"
|
||||
"OVERLAPS"
|
||||
"RULES"
|
||||
"SOURCE"
|
||||
"SPECIALIZE"
|
||||
"UNPACK"
|
||||
"WARNING")
|
||||
"A list of supported pragmas.
|
||||
This list comes from GHC documentation (URL
|
||||
`https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/pragmas.html'.")
|
||||
|
||||
(defvar haskell-completions--keywords
|
||||
(list
|
||||
"as"
|
||||
"case"
|
||||
"class"
|
||||
"data family"
|
||||
"data instance"
|
||||
"data"
|
||||
"default"
|
||||
"deriving instance"
|
||||
"deriving"
|
||||
"do"
|
||||
"else"
|
||||
"family"
|
||||
"forall"
|
||||
"foreign import"
|
||||
"foreign"
|
||||
"hiding"
|
||||
"if"
|
||||
"import qualified"
|
||||
"import"
|
||||
"in"
|
||||
"infix"
|
||||
"infixl"
|
||||
"infixr"
|
||||
"instance"
|
||||
"let"
|
||||
"mdo"
|
||||
"module"
|
||||
"newtype"
|
||||
"of"
|
||||
"proc"
|
||||
"qualified"
|
||||
"rec"
|
||||
"then"
|
||||
"type family"
|
||||
"type instance"
|
||||
"type"
|
||||
"where")
|
||||
"A list of Haskell's keywords (URL `https://wiki.haskell.org/Keywords').
|
||||
Single char keywords and operator like keywords are not included
|
||||
in this list.")
|
||||
|
||||
|
||||
(defun haskell-completions-can-grab-prefix ()
|
||||
"Check if the case is appropriate for grabbing completion prefix.
|
||||
Returns t if point is either at whitespace character, or at
|
||||
punctuation, or at line end and preceeding character is not a
|
||||
whitespace or new line, otherwise returns nil.
|
||||
|
||||
Returns nil in presence of active region."
|
||||
(when (not (region-active-p))
|
||||
(when (looking-at-p (rx (| space line-end punct)))
|
||||
(when (not (bobp))
|
||||
(save-excursion
|
||||
(backward-char)
|
||||
(not (looking-at-p (rx (| space line-end)))))))))
|
||||
|
||||
(defun haskell-completions-grab-pragma-prefix ()
|
||||
"Grab completion prefix for pragma completions.
|
||||
Returns a list of form '(prefix-start-position
|
||||
prefix-end-position prefix-value prefix-type) for pramga names
|
||||
such as WARNING, DEPRECATED, LANGUAGE etc. Also returns
|
||||
completion prefixes for options in case OPTIONS_GHC pragma, or
|
||||
language extensions in case of LANGUAGE pragma. Obsolete OPTIONS
|
||||
pragma is supported also."
|
||||
(when (nth 4 (syntax-ppss))
|
||||
;; We're inside comment
|
||||
(let ((p (point))
|
||||
(comment-start (nth 8 (syntax-ppss)))
|
||||
(case-fold-search nil)
|
||||
prefix-start
|
||||
prefix-end
|
||||
prefix-type
|
||||
prefix-value)
|
||||
(save-excursion
|
||||
(goto-char comment-start)
|
||||
(when (looking-at (rx "{-#" (1+ (| space "\n"))))
|
||||
(let ((pragma-start (match-end 0)))
|
||||
(when (> p pragma-start)
|
||||
;; point stands after `{-#`
|
||||
(goto-char pragma-start)
|
||||
(when (looking-at (rx (1+ (| upper "_"))))
|
||||
;; found suitable sequence for pragma name
|
||||
(let ((pragma-end (match-end 0))
|
||||
(pragma-value (match-string-no-properties 0)))
|
||||
(if (eq p pragma-end)
|
||||
;; point is at the end of (in)complete pragma name
|
||||
;; prepare resulting values
|
||||
(progn
|
||||
(setq prefix-start pragma-start)
|
||||
(setq prefix-end pragma-end)
|
||||
(setq prefix-value pragma-value)
|
||||
(setq prefix-type
|
||||
'haskell-completions-pragma-name-prefix))
|
||||
(when (and (> p pragma-end)
|
||||
(or (equal "OPTIONS_GHC" pragma-value)
|
||||
(equal "OPTIONS" pragma-value)
|
||||
(equal "LANGUAGE" pragma-value)))
|
||||
;; point is after pragma name, so we need to check
|
||||
;; special cases of `OPTIONS_GHC` and `LANGUAGE` pragmas
|
||||
;; and provide a completion prefix for possible ghc
|
||||
;; option or language extension.
|
||||
(goto-char pragma-end)
|
||||
(when (re-search-forward
|
||||
(rx (* anything)
|
||||
(1+ (regexp "\\S-")))
|
||||
p
|
||||
t)
|
||||
(let* ((str (match-string-no-properties 0))
|
||||
(split (split-string str (rx (| space "\n")) t))
|
||||
(val (car (last split)))
|
||||
(end (point)))
|
||||
(when (and (equal p end)
|
||||
(not (string-match-p "#" val)))
|
||||
(setq prefix-value val)
|
||||
(backward-char (length val))
|
||||
(setq prefix-start (point))
|
||||
(setq prefix-end end)
|
||||
(setq
|
||||
prefix-type
|
||||
(if (not (equal "LANGUAGE" pragma-value))
|
||||
'haskell-completions-ghc-option-prefix
|
||||
'haskell-completions-language-extension-prefix
|
||||
)))))))))))))
|
||||
(when prefix-value
|
||||
(list prefix-start prefix-end prefix-value prefix-type)))))
|
||||
|
||||
(defun haskell-completions-grab-identifier-prefix ()
|
||||
"Grab completion prefix for identifier at point.
|
||||
Returns a list of form '(prefix-start-position
|
||||
prefix-end-position prefix-value prefix-type) for haskell
|
||||
identifier at point depending on result of function
|
||||
`haskell-ident-pos-at-point'."
|
||||
(let ((pos-at-point (haskell-ident-pos-at-point))
|
||||
(p (point)))
|
||||
(when pos-at-point
|
||||
(let* ((start (car pos-at-point))
|
||||
(end (cdr pos-at-point))
|
||||
(type 'haskell-completions-identifier-prefix)
|
||||
(case-fold-search nil)
|
||||
value)
|
||||
;; we need end position of result, becase of
|
||||
;; `haskell-ident-pos-at-point' ignores trailing whitespace, e.g. the
|
||||
;; result will be same for `map|` and `map |` invocations.
|
||||
(when (<= p end)
|
||||
(setq end p)
|
||||
(setq value (buffer-substring-no-properties start end))
|
||||
(when (string-match-p (rx bos upper) value)
|
||||
;; we need to check if found identifier is a module name
|
||||
(save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(when (re-search-forward
|
||||
(rx "import"
|
||||
(? (1+ space) "qualified")
|
||||
(1+ space)
|
||||
upper
|
||||
(1+ (| alnum ".")))
|
||||
p ;; bound
|
||||
t) ;; no-error
|
||||
(if (equal p (point))
|
||||
(setq type 'haskell-completions-module-name-prefix)
|
||||
(when (re-search-forward
|
||||
(rx (| " as " "("))
|
||||
start
|
||||
t)
|
||||
;; but uppercase ident could occur after `as` keyword, or in
|
||||
;; module imports after opening parenthesis, in this case
|
||||
;; restore identifier type again, it's neccessary to
|
||||
;; distinguish the means of completions retrieval
|
||||
(setq type 'haskell-completions-identifier-prefix))))))
|
||||
(when (nth 8 (syntax-ppss))
|
||||
;; eighth element of syntax-ppss result is string or comment start,
|
||||
;; so when it's not nil word at point is inside string or comment,
|
||||
;; return special literal prefix type
|
||||
(setq type 'haskell-completions-general-prefix))
|
||||
;; finally take in account minlen if given and return the result
|
||||
(when value (list start end value type)))))))
|
||||
|
||||
(defun haskell-completions-grab-prefix (&optional minlen)
|
||||
"Grab prefix at point for possible completion.
|
||||
Returns a list of form '(prefix-start-position
|
||||
prefix-end-position prefix-value prefix-type) depending on
|
||||
situation, e.g. is it needed to complete pragma, module name,
|
||||
arbitrary identifier, etc. Returns nil in case it is
|
||||
impossible to grab prefix.
|
||||
|
||||
Possible prefix types are:
|
||||
|
||||
* haskell-completions-pragma-name-prefix
|
||||
* haskell-completions-ghc-option-prefix
|
||||
* haskell-completions-language-extension-prefix
|
||||
* haskell-completions-module-name-prefix
|
||||
* haskell-completions-identifier-prefix
|
||||
* haskell-completions-general-prefix
|
||||
|
||||
the last type is used in cases when completing things inside comments.
|
||||
|
||||
If provided optional MINLEN parameter this function will return
|
||||
result only if prefix length is not less than MINLEN."
|
||||
(when (haskell-completions-can-grab-prefix)
|
||||
(let ((prefix (cond
|
||||
((haskell-completions-grab-pragma-prefix))
|
||||
((haskell-completions-grab-identifier-prefix)))))
|
||||
(cond ((and minlen prefix)
|
||||
(when (>= (length (nth 2 prefix)) minlen)
|
||||
prefix))
|
||||
(prefix prefix)))))
|
||||
|
||||
(defun haskell-completions--simple-completions (prefix)
|
||||
"Provide a list of completion candidates for given PREFIX.
|
||||
This function is used internally in
|
||||
`haskell-completions-completion-at-point' and
|
||||
`haskell-completions-sync-repl-completion-at-point'.
|
||||
|
||||
It provides completions for haskell keywords, language pragmas,
|
||||
GHC's options, and language extensions.
|
||||
|
||||
PREFIX should be a list such one returned by
|
||||
`haskell-completions-grab-identifier-prefix'."
|
||||
(cl-destructuring-bind (beg end _pfx typ) prefix
|
||||
(when (not (eql typ 'haskell-completions-general-prefix))
|
||||
(let ((candidates
|
||||
(cl-case typ
|
||||
('haskell-completions-pragma-name-prefix
|
||||
haskell-completions--pragma-names)
|
||||
('haskell-completions-ghc-option-prefix
|
||||
haskell-ghc-supported-options)
|
||||
('haskell-completions-language-extension-prefix
|
||||
haskell-ghc-supported-extensions)
|
||||
(otherwise
|
||||
(append (when (bound-and-true-p haskell-tags-on-save)
|
||||
tags-completion-table)
|
||||
haskell-completions--keywords)))))
|
||||
(list beg end candidates)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-completions-completion-at-point ()
|
||||
"Provide completion list for thing at point.
|
||||
This function is used in non-interactive `haskell-mode'. It
|
||||
provides completions for haskell keywords, language pragmas,
|
||||
GHC's options, and language extensions, but not identifiers."
|
||||
(let ((prefix (haskell-completions-grab-prefix)))
|
||||
(when prefix
|
||||
(haskell-completions--simple-completions prefix))))
|
||||
|
||||
(defun haskell-completions-sync-repl-completion-at-point ()
|
||||
"A completion function used in `interactive-haskell-mode'.
|
||||
Completion candidates are provided quering current haskell
|
||||
process, that is sending `:complete repl' command.
|
||||
|
||||
Completes all possible things: everything that can be completed
|
||||
with non-interactive function
|
||||
`haskell-completions-completion-at-point' plus identifier
|
||||
completions.
|
||||
|
||||
Returns nil if no completions available."
|
||||
(let ((prefix-data (haskell-completions-grab-prefix)))
|
||||
(when prefix-data
|
||||
(cl-destructuring-bind (beg end pfx typ) prefix-data
|
||||
(when (and (not (eql typ 'haskell-completions-general-prefix))
|
||||
(or haskell-completions-complete-operators
|
||||
(not (save-excursion
|
||||
(goto-char (1- end))
|
||||
(haskell-mode--looking-at-varsym)))))
|
||||
;; do not complete things in comments
|
||||
(if (cl-member
|
||||
typ
|
||||
'(haskell-completions-pragma-name-prefix
|
||||
haskell-completions-ghc-option-prefix
|
||||
haskell-completions-language-extension-prefix))
|
||||
;; provide simple completions
|
||||
(haskell-completions--simple-completions prefix-data)
|
||||
;; only two cases left: haskell-completions-module-name-prefix
|
||||
;; and haskell-completions-identifier-prefix
|
||||
(let* ((is-import (eql typ 'haskell-completions-module-name-prefix))
|
||||
(candidates
|
||||
(when (and (haskell-session-maybe)
|
||||
(not (haskell-process-cmd
|
||||
(haskell-interactive-process)))
|
||||
;; few possible extra checks would be:
|
||||
;; (haskell-process-get 'is-restarting)
|
||||
;; (haskell-process-get 'evaluating)
|
||||
)
|
||||
;; if REPL is available and not busy try to query it for
|
||||
;; completions list in case of module name or identifier
|
||||
;; prefixes
|
||||
(haskell-completions-sync-complete-repl pfx is-import))))
|
||||
;; append candidates with keywords
|
||||
(list beg end (append
|
||||
candidates
|
||||
haskell-completions--keywords)))))))))
|
||||
|
||||
(defun haskell-completions-sync-complete-repl (prefix &optional import)
|
||||
"Return completion list for given PREFIX querying REPL synchronously.
|
||||
When optional IMPORT argument is non-nil complete PREFIX
|
||||
prepending \"import \" keyword (useful for module names). This
|
||||
function is supposed for internal use."
|
||||
(haskell-process-get-repl-completions
|
||||
(haskell-interactive-process)
|
||||
(if import
|
||||
(concat "import " prefix)
|
||||
prefix)))
|
||||
|
||||
(provide 'haskell-completions)
|
||||
;;; haskell-completions.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-completions.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-completions.elc
Normal file
Binary file not shown.
467
elpa/haskell-mode-20171022.26/haskell-customize.el
Normal file
467
elpa/haskell-mode-20171022.26/haskell-customize.el
Normal file
@@ -0,0 +1,467 @@
|
||||
;;; haskell-customize.el --- Customization settings -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Customization variables
|
||||
|
||||
(defcustom haskell-process-load-or-reload-prompt nil
|
||||
"Nil means there will be no prompts on starting REPL. Defaults will be accepted."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell nil
|
||||
"Major mode for editing Haskell programs."
|
||||
:link '(custom-manual "(haskell-mode)")
|
||||
:group 'languages
|
||||
:prefix "haskell-")
|
||||
|
||||
(defvar haskell-mode-pkg-base-dir (file-name-directory load-file-name)
|
||||
"Package base directory of installed `haskell-mode'.
|
||||
Used for locating additional package data files.")
|
||||
|
||||
(defcustom haskell-completing-read-function 'ido-completing-read
|
||||
"Default function to use for completion."
|
||||
:group 'haskell
|
||||
:type '(choice
|
||||
(function-item :tag "ido" :value ido-completing-read)
|
||||
(function-item :tag "helm" :value helm--completing-read-default)
|
||||
(function-item :tag "completing-read" :value completing-read)
|
||||
(function :tag "Custom function")))
|
||||
|
||||
(defcustom haskell-process-type
|
||||
'auto
|
||||
"The inferior Haskell process type to use.
|
||||
|
||||
When set to 'auto (the default), the directory contents and
|
||||
available programs will be used to make a best guess at the
|
||||
process type:
|
||||
|
||||
If the project directory or one of its parents contains a
|
||||
\"cabal.sandbox.config\" file, then cabal-repl will be used.
|
||||
|
||||
If there's a \"stack.yaml\" file and the \"stack\" executable can
|
||||
be located, then stack-ghci will be used.
|
||||
|
||||
Otherwise if there's a *.cabal file, cabal-repl will be used.
|
||||
|
||||
If none of the above apply, ghci will be used."
|
||||
:type '(choice (const auto)
|
||||
(const ghci)
|
||||
(const cabal-repl)
|
||||
(const stack-ghci)
|
||||
(const cabal-new-repl))
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-wrapper-function
|
||||
#'identity
|
||||
"Wrap or transform haskell process commands using this function.
|
||||
|
||||
Can be set to a custom function which takes a list of arguments
|
||||
and returns a possibly-modified list.
|
||||
|
||||
The following example function arranges for all haskell process
|
||||
commands to be started in the current nix-shell environment:
|
||||
|
||||
(lambda (argv) (append (list \"nix-shell\" \"-I\" \".\" \"--command\" )
|
||||
(list (mapconcat 'identity argv \" \"))))
|
||||
|
||||
See Info Node `(emacs)Directory Variables' for a way to set this option on
|
||||
a per-project basis."
|
||||
:group 'haskell-interactive
|
||||
:type '(choice
|
||||
(function-item :tag "None" :value identity)
|
||||
(function :tag "Custom function"))
|
||||
:safe 'functionp)
|
||||
|
||||
(defcustom haskell-ask-also-kill-buffers
|
||||
t
|
||||
"Ask whether to kill all associated buffers when a session
|
||||
process is killed."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Configuration
|
||||
|
||||
(defcustom haskell-doc-prettify-types t
|
||||
"Replace some parts of types with Unicode characters like \"∷\"
|
||||
when showing type information about symbols."
|
||||
:group 'haskell-doc
|
||||
:type 'boolean
|
||||
:safe 'booleanp)
|
||||
|
||||
(defvar haskell-process-ended-functions (list 'haskell-process-prompt-restart)
|
||||
"Hook for when the haskell process ends.")
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell-interactive nil
|
||||
"Settings for REPL interaction via `haskell-interactive-mode'"
|
||||
:link '(custom-manual "(haskell-mode)haskell-interactive-mode")
|
||||
:group 'haskell)
|
||||
|
||||
(defcustom haskell-process-path-ghci
|
||||
"ghci"
|
||||
"The path for starting ghci.
|
||||
This can either be a single string or a list of strings, where the
|
||||
first elements is a string and the remaining elements are arguments,
|
||||
which will be prepended to `haskell-process-args-ghci'."
|
||||
:group 'haskell-interactive
|
||||
:type '(choice string (repeat string)))
|
||||
|
||||
(defcustom haskell-process-path-cabal
|
||||
"cabal"
|
||||
"Path to the `cabal' executable.
|
||||
This can either be a single string or a list of strings, where the
|
||||
first elements is a string and the remaining elements are arguments,
|
||||
which will be prepended to `haskell-process-args-cabal-repl'."
|
||||
:group 'haskell-interactive
|
||||
:type '(choice string (repeat string)))
|
||||
|
||||
(defcustom haskell-process-path-stack
|
||||
"stack"
|
||||
"The path for starting stack.
|
||||
This can either be a single string or a list of strings, where the
|
||||
first elements is a string and the remaining elements are arguments,
|
||||
which will be prepended to `haskell-process-args-stack-ghci'."
|
||||
:group 'haskell-interactive
|
||||
:type '(choice string (repeat string)))
|
||||
|
||||
(defcustom haskell-process-args-ghci
|
||||
'("-ferror-spans")
|
||||
"Any arguments for starting ghci."
|
||||
:group 'haskell-interactive
|
||||
:type '(repeat (string :tag "Argument")))
|
||||
|
||||
(defcustom haskell-process-args-cabal-repl
|
||||
'("--ghc-option=-ferror-spans")
|
||||
"Additional arguments for `cabal repl' invocation.
|
||||
Note: The settings in `haskell-process-path-ghci' and
|
||||
`haskell-process-args-ghci' are not automatically reused as `cabal repl'
|
||||
currently invokes `ghc --interactive'. Use
|
||||
`--with-ghc=<path-to-executable>' if you want to use a different
|
||||
interactive GHC frontend; use `--ghc-option=<ghc-argument>' to
|
||||
pass additional flags to `ghc'."
|
||||
:group 'haskell-interactive
|
||||
:type '(repeat (string :tag "Argument")))
|
||||
|
||||
(defcustom haskell-process-args-cabal-new-repl
|
||||
'("--ghc-option=-ferror-spans")
|
||||
"Additional arguments for `cabal new-repl' invocation.
|
||||
Note: The settings in `haskell-process-path-ghci' and
|
||||
`haskell-process-args-ghci' are not automatically reused as
|
||||
`cabal new-repl' currently invokes `ghc --interactive'. Use
|
||||
`--with-ghc=<path-to-executable>' if you want to use a different
|
||||
interactive GHC frontend; use `--ghc-option=<ghc-argument>' to
|
||||
pass additional flags to `ghc'."
|
||||
:group 'haskell-interactive
|
||||
:type '(repeat (string :tag "Argument")))
|
||||
|
||||
(defcustom haskell-process-args-stack-ghci
|
||||
'("--ghci-options=-ferror-spans" "--no-build" "--no-load")
|
||||
"Additional arguments for `stack ghci' invocation."
|
||||
:group 'haskell-interactive
|
||||
:type '(repeat (string :tag "Argument")))
|
||||
|
||||
(defcustom haskell-process-do-cabal-format-string
|
||||
":!cd %s && %s"
|
||||
"The way to run cabal comands. It takes two arguments -- the directory and the command.
|
||||
See `haskell-process-do-cabal' for more details."
|
||||
:group 'haskell-interactive
|
||||
:type 'string)
|
||||
|
||||
(defcustom haskell-process-log
|
||||
nil
|
||||
"Enable debug logging to \"*haskell-process-log*\" buffer."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-show-debug-tips
|
||||
t
|
||||
"Show debugging tips when starting the process."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-show-overlays
|
||||
t
|
||||
"Show in-buffer overlays for errors/warnings.
|
||||
Flycheck users might like to disable this."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-notify-p
|
||||
nil
|
||||
"Notify using notifications.el (if loaded)?"
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-no-warn-orphans
|
||||
t
|
||||
"Suggest adding -fno-warn-orphans pragma to file when getting orphan warnings."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-hoogle-imports
|
||||
nil
|
||||
"Suggest to add import statements using Hoogle as a backend."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-hayoo-imports
|
||||
nil
|
||||
"Suggest to add import statements using Hayoo as a backend."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-hayoo-query-url
|
||||
"http://hayoo.fh-wedel.de/json/?query=%s"
|
||||
"Query url for json hayoo results."
|
||||
:type 'string
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-haskell-docs-imports
|
||||
nil
|
||||
"Suggest to add import statements using haskell-docs as a backend."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-add-package
|
||||
t
|
||||
"Suggest to add packages to your .cabal file when Cabal says it
|
||||
is a member of the hidden package, blah blah."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-language-pragmas
|
||||
t
|
||||
"Suggest adding LANGUAGE pragmas recommended by GHC."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-remove-import-lines
|
||||
nil
|
||||
"Suggest removing import lines as warned by GHC."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-overloaded-strings
|
||||
t
|
||||
"Suggest adding OverloadedStrings pragma to file when getting type mismatches with [Char]."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-check-cabal-config-on-load
|
||||
t
|
||||
"Check changes cabal config on loading Haskell files and
|
||||
restart the GHCi process if changed.."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-prompt-restart-on-cabal-change
|
||||
t
|
||||
"Ask whether to restart the GHCi process when the Cabal file
|
||||
has changed?"
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-auto-import-loaded-modules
|
||||
nil
|
||||
"Auto import the modules reported by GHC to have been loaded?"
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-reload-with-fbytecode
|
||||
nil
|
||||
"When using -fobject-code, auto reload with -fbyte-code (and
|
||||
then restore the -fobject-code) so that all module info and
|
||||
imports become available?"
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-use-presentation-mode
|
||||
nil
|
||||
"Use presentation mode to show things like type info instead of
|
||||
printing to the message area."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-process-suggest-restart
|
||||
t
|
||||
"Suggest restarting the process when it has died"
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-popup-errors
|
||||
t
|
||||
"Popup errors in a separate buffer."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-mode-collapse
|
||||
nil
|
||||
"Collapse printed results."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-types-for-show-ambiguous
|
||||
t
|
||||
"Show types when there's no Show instance or there's an
|
||||
ambiguous class constraint."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-prompt "λ> "
|
||||
"The prompt to use."
|
||||
:type 'string
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-prompt2 (replace-regexp-in-string
|
||||
"> $"
|
||||
"| "
|
||||
haskell-interactive-prompt)
|
||||
"The multi-line prompt to use.
|
||||
The default is `haskell-interactive-prompt' with the last > replaced with |."
|
||||
:type 'string
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-mode-eval-mode
|
||||
nil
|
||||
"Use the given mode's font-locking to render some text."
|
||||
:type '(choice function (const :tag "None" nil))
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-mode-hide-multi-line-errors
|
||||
nil
|
||||
"Hide collapsible multi-line compile messages by default."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-mode-delete-superseded-errors
|
||||
t
|
||||
"Whether to delete compile messages superseded by recompile/reloads."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-mode-include-file-name
|
||||
t
|
||||
"Include the file name of the module being compiled when
|
||||
printing compilation messages."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-mode-read-only
|
||||
t
|
||||
"Non-nil means most GHCi/haskell-interactive-mode output is read-only.
|
||||
This does not include the prompt. Configure
|
||||
`haskell-interactive-prompt-read-only' to change the prompt's
|
||||
read-only property."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-interactive-prompt-read-only
|
||||
haskell-interactive-mode-read-only
|
||||
"Non-nil means the prompt (and prompt2) is read-only."
|
||||
:type 'boolean
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-import-mapping
|
||||
'()
|
||||
"Support a mapping from module to import lines.
|
||||
|
||||
E.g. '((\"Data.Map\" . \"import qualified Data.Map as M
|
||||
import Data.Map (Map)
|
||||
\"))
|
||||
|
||||
This will import
|
||||
|
||||
import qualified Data.Map as M
|
||||
import Data.Map (Map)
|
||||
|
||||
when Data.Map is the candidate.
|
||||
|
||||
"
|
||||
:type '(repeat (cons (string :tag "Module name")
|
||||
(string :tag "Import lines")))
|
||||
:group 'haskell-interactive)
|
||||
|
||||
(defcustom haskell-language-extensions
|
||||
'()
|
||||
"Language extensions in use. Should be in format: -XFoo,
|
||||
-XNoFoo etc. The idea is that various tools written with HSE (or
|
||||
any haskell-mode code that needs to be aware of syntactical
|
||||
properties; such as an indentation mode) that don't know what
|
||||
extensions to use can use this variable. Examples: hlint,
|
||||
hindent, structured-haskell-mode, tool-de-jour, etc.
|
||||
|
||||
You can set this per-project with a .dir-locals.el file"
|
||||
:group 'haskell
|
||||
:type '(repeat 'string))
|
||||
|
||||
(defcustom haskell-stylish-on-save nil
|
||||
"Whether to run stylish-haskell on the buffer before saving.
|
||||
If this is true, `haskell-add-import' will not sort or align the
|
||||
imports."
|
||||
:group 'haskell
|
||||
:type 'boolean)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Accessor functions
|
||||
|
||||
(defvar inferior-haskell-root-dir nil
|
||||
"The path which is considered as project root, this is determined by the
|
||||
presence of a *.cabal file or stack.yaml file or something similar.")
|
||||
|
||||
(defun haskell-process-type ()
|
||||
"Return `haskell-process-type', or a guess if that variable is 'auto.
|
||||
This function also sets the `inferior-haskell-root-dir'"
|
||||
(let ((cabal-sandbox (locate-dominating-file default-directory
|
||||
"cabal.sandbox.config"))
|
||||
(stack (locate-dominating-file default-directory
|
||||
"stack.yaml"))
|
||||
(cabal (locate-dominating-file default-directory
|
||||
(lambda (d)
|
||||
(cl-find-if
|
||||
(lambda (f)
|
||||
(string-match-p ".\\.cabal\\'" f))
|
||||
(directory-files d))))))
|
||||
(if (eq 'auto haskell-process-type)
|
||||
(cond
|
||||
;; User has explicitly initialized this project with cabal
|
||||
((and cabal-sandbox
|
||||
(executable-find "cabal"))
|
||||
(setq inferior-haskell-root-dir cabal-sandbox)
|
||||
'cabal-repl)
|
||||
((and stack
|
||||
(executable-find "stack"))
|
||||
(setq inferior-haskell-root-dir stack)
|
||||
'stack-ghci)
|
||||
((and cabal
|
||||
(executable-find "cabal"))
|
||||
(setq inferior-haskell-root-dir cabal)
|
||||
'cabal-repl)
|
||||
((executable-find "ghc")
|
||||
(setq inferior-haskell-root-dir default-directory)
|
||||
'ghci)
|
||||
(t
|
||||
(error "Could not find any installation of GHC.")))
|
||||
haskell-process-type)))
|
||||
|
||||
(provide 'haskell-customize)
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-customize.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-customize.elc
Normal file
Binary file not shown.
757
elpa/haskell-mode-20171022.26/haskell-debug.el
Normal file
757
elpa/haskell-mode-20171022.26/haskell-debug.el
Normal file
@@ -0,0 +1,757 @@
|
||||
;;; haskell-debug.el --- Debugging mode via GHCi -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright © 2014 Chris Done. All rights reserved.
|
||||
;; 2016 Arthur Fayzrakhmanov
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-session)
|
||||
(require 'haskell-process)
|
||||
(require 'haskell-interactive-mode)
|
||||
(require 'haskell-font-lock)
|
||||
(require 'haskell-utils)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Configuration
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell-debug nil
|
||||
"Settings for debugging support."
|
||||
:link '(custom-manual "(haskell-mode)haskell-debug")
|
||||
:group 'haskell)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-debug-warning-face
|
||||
'((t :inherit 'compilation-warning))
|
||||
"Face for warnings."
|
||||
:group 'haskell-debug)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-debug-trace-number-face
|
||||
'((t :weight bold :background "#f5f5f5"))
|
||||
"Face for numbers in backtrace."
|
||||
:group 'haskell-debug)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-debug-newline-face
|
||||
'((t :weight bold :background "#f0f0f0"))
|
||||
"Face for newlines in trace steps."
|
||||
:group 'haskell-debug)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-debug-keybinding-face
|
||||
'((t :inherit 'font-lock-type-face :weight bold))
|
||||
"Face for keybindings."
|
||||
:group 'haskell-debug)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-debug-heading-face
|
||||
'((t :inherit 'font-lock-keyword-face))
|
||||
"Face for headings."
|
||||
:group 'haskell-debug)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-debug-muted-face
|
||||
'((t :foreground "#999"))
|
||||
"Face for muteds."
|
||||
:group 'haskell-debug)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mode
|
||||
|
||||
(defvar haskell-debug-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "g") 'haskell-debug/refresh)
|
||||
(define-key map (kbd "s") 'haskell-debug/step)
|
||||
(define-key map (kbd "t") 'haskell-debug/trace)
|
||||
(define-key map (kbd "d") 'haskell-debug/delete)
|
||||
(define-key map (kbd "b") 'haskell-debug/break-on-function)
|
||||
(define-key map (kbd "a") 'haskell-debug/abandon)
|
||||
(define-key map (kbd "c") 'haskell-debug/continue)
|
||||
(define-key map (kbd "p") 'haskell-debug/previous)
|
||||
(define-key map (kbd "n") 'haskell-debug/next)
|
||||
(define-key map (kbd "RET") 'haskell-debug/select)
|
||||
map)
|
||||
"Keymap for `haskell-debug-mode'.")
|
||||
|
||||
(define-derived-mode haskell-debug-mode
|
||||
text-mode "Debug"
|
||||
"Major mode for debugging Haskell via GHCi.")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Globals
|
||||
|
||||
(defvar haskell-debug-history-cache nil
|
||||
"Cache of the tracing history.")
|
||||
|
||||
(defvar haskell-debug-bindings-cache nil
|
||||
"Cache of the current step's bindings.")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Macros
|
||||
|
||||
(defmacro haskell-debug-with-breakpoints (&rest body)
|
||||
"Breakpoints need to exist to start stepping."
|
||||
`(if (haskell-debug-get-breakpoints)
|
||||
,@body
|
||||
(error "No breakpoints to step into!")))
|
||||
|
||||
(defmacro haskell-debug-with-modules (&rest body)
|
||||
"Modules need to exist to do debugging stuff."
|
||||
`(if (haskell-debug-get-modules)
|
||||
,@body
|
||||
(error "No modules loaded!")))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Interactive functions
|
||||
|
||||
(defun haskell-debug/select ()
|
||||
"Select whatever is at point."
|
||||
(interactive)
|
||||
(cond
|
||||
((get-text-property (point) 'break)
|
||||
(let ((break (get-text-property (point) 'break)))
|
||||
(haskell-debug-highlight (plist-get break :path)
|
||||
(plist-get break :span))))
|
||||
((get-text-property (point) 'module)
|
||||
(let ((break (get-text-property (point) 'module)))
|
||||
(haskell-debug-highlight (plist-get break :path))))))
|
||||
|
||||
(defun haskell-debug/abandon ()
|
||||
"Abandon the current computation."
|
||||
(interactive)
|
||||
(haskell-debug-with-breakpoints
|
||||
(haskell-process-queue-sync-request (haskell-debug-process) ":abandon")
|
||||
(message "Computation abandoned.")
|
||||
(setq haskell-debug-history-cache nil)
|
||||
(setq haskell-debug-bindings-cache nil)
|
||||
(haskell-debug/refresh)))
|
||||
|
||||
(defun haskell-debug/continue ()
|
||||
"Continue the current computation."
|
||||
(interactive)
|
||||
(haskell-debug-with-breakpoints
|
||||
(haskell-process-queue-sync-request (haskell-debug-process) ":continue")
|
||||
(message "Computation continued.")
|
||||
(setq haskell-debug-history-cache nil)
|
||||
(setq haskell-debug-bindings-cache nil)
|
||||
(haskell-debug/refresh)))
|
||||
|
||||
(defun haskell-debug/break-on-function ()
|
||||
"Break on function IDENT."
|
||||
(interactive)
|
||||
(haskell-debug-with-modules
|
||||
(let ((ident (read-from-minibuffer "Function: "
|
||||
(haskell-ident-at-point))))
|
||||
(haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
(concat ":break "
|
||||
ident))
|
||||
(message "Breaking on function: %s" ident)
|
||||
(haskell-debug/refresh))))
|
||||
|
||||
(defun haskell-debug/start-step (expr)
|
||||
"Start stepping EXPR."
|
||||
(interactive (list (read-from-minibuffer "Expression to step through: ")))
|
||||
(haskell-debug/step expr))
|
||||
|
||||
(defun haskell-debug/breakpoint-numbers ()
|
||||
"List breakpoint numbers."
|
||||
(interactive)
|
||||
(let ((breakpoints (mapcar (lambda (breakpoint)
|
||||
(number-to-string (plist-get breakpoint :number)))
|
||||
(haskell-debug-get-breakpoints))))
|
||||
(if (null breakpoints)
|
||||
(message "No breakpoints.")
|
||||
(message "Breakpoint(s): %s"
|
||||
(mapconcat #'identity
|
||||
breakpoints
|
||||
", ")))))
|
||||
|
||||
(defun haskell-debug/next ()
|
||||
"Go to next step to inspect bindings."
|
||||
(interactive)
|
||||
(haskell-debug-with-breakpoints
|
||||
(haskell-debug-navigate "forward")))
|
||||
|
||||
(defun haskell-debug/previous ()
|
||||
"Go to previous step to inspect the bindings."
|
||||
(interactive)
|
||||
(haskell-debug-with-breakpoints
|
||||
(haskell-debug-navigate "back")))
|
||||
|
||||
(defun haskell-debug/refresh ()
|
||||
"Refresh the debugger buffer."
|
||||
(interactive)
|
||||
(with-current-buffer (haskell-debug-buffer-name (haskell-debug-session))
|
||||
(cd (haskell-session-current-dir (haskell-debug-session)))
|
||||
(let ((inhibit-read-only t)
|
||||
(p (point)))
|
||||
(erase-buffer)
|
||||
(insert (propertize (concat "Debugging "
|
||||
(haskell-session-name (haskell-debug-session))
|
||||
"\n\n")
|
||||
'face `((:weight bold))))
|
||||
(let ((modules (haskell-debug-get-modules))
|
||||
(breakpoints (haskell-debug-get-breakpoints))
|
||||
(context (haskell-debug-get-context))
|
||||
(history (haskell-debug-get-history)))
|
||||
(unless modules
|
||||
(insert (propertize "You have to load a module to start debugging."
|
||||
'face
|
||||
'haskell-debug-warning-face)
|
||||
"\n\n"))
|
||||
(haskell-debug-insert-bindings modules breakpoints context)
|
||||
(when modules
|
||||
(haskell-debug-insert-current-context context history)
|
||||
(haskell-debug-insert-breakpoints breakpoints))
|
||||
(haskell-debug-insert-modules modules))
|
||||
(insert "\n")
|
||||
(goto-char (min (point-max) p)))))
|
||||
|
||||
(defun haskell-debug/delete ()
|
||||
"Delete whatever's at the point."
|
||||
(interactive)
|
||||
(cond
|
||||
((get-text-property (point) 'break)
|
||||
(let ((break (get-text-property (point) 'break)))
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(when (y-or-n-p (format "Delete breakpoint #%d?"
|
||||
(plist-get break :number)))
|
||||
(haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
(format ":delete %d"
|
||||
(plist-get break :number)))
|
||||
(haskell-debug/refresh))
|
||||
(haskell-mode-toggle-interactive-prompt-state t))))))
|
||||
|
||||
(defun haskell-debug/trace ()
|
||||
"Trace the expression."
|
||||
(interactive)
|
||||
(haskell-debug-with-modules
|
||||
(haskell-debug-with-breakpoints
|
||||
(let ((expr (read-from-minibuffer "Expression to trace: "
|
||||
(haskell-ident-at-point))))
|
||||
(haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
(concat ":trace " expr))
|
||||
(message "Tracing expression: %s" expr)
|
||||
(haskell-debug/refresh)))))
|
||||
|
||||
(defun haskell-debug/step (&optional expr)
|
||||
"Step into the next function."
|
||||
(interactive)
|
||||
(haskell-debug-with-breakpoints
|
||||
(let* ((breakpoints (haskell-debug-get-breakpoints))
|
||||
(context (haskell-debug-get-context))
|
||||
(string
|
||||
(haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
(if expr
|
||||
(concat ":step " expr)
|
||||
":step"))))
|
||||
(cond
|
||||
((string= string "not stopped at a breakpoint\n")
|
||||
(if haskell-debug-bindings-cache
|
||||
(progn (setq haskell-debug-bindings-cache nil)
|
||||
(haskell-debug/refresh))
|
||||
(call-interactively 'haskell-debug/start-step)))
|
||||
(t (let ((maybe-stopped-at (haskell-debug-parse-stopped-at string)))
|
||||
(cond
|
||||
(maybe-stopped-at
|
||||
(setq haskell-debug-bindings-cache
|
||||
maybe-stopped-at)
|
||||
(message "Computation paused.")
|
||||
(haskell-debug/refresh))
|
||||
(t
|
||||
(if context
|
||||
(message "Computation finished.")
|
||||
(progn
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(when (y-or-n-p "Computation completed without breaking. Reload the module and retry?")
|
||||
(message "Reloading and resetting breakpoints...")
|
||||
(haskell-interactive-mode-reset-error (haskell-debug-session))
|
||||
(cl-loop for break in breakpoints
|
||||
do (haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
(concat ":load " (plist-get break :path))))
|
||||
(cl-loop for break in breakpoints
|
||||
do (haskell-debug-break break))
|
||||
(haskell-debug/step expr))
|
||||
(haskell-mode-toggle-interactive-prompt-state t))))))))))
|
||||
(haskell-debug/refresh)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Internal functions
|
||||
|
||||
(defun haskell-debug-session ()
|
||||
"Get the Haskell session."
|
||||
(or (haskell-session-maybe)
|
||||
(error "No Haskell session associated with this debug
|
||||
buffer. Please just close the buffer and start again.")))
|
||||
|
||||
(defun haskell-debug-process ()
|
||||
"Get the Haskell session."
|
||||
(or (haskell-session-process (haskell-session-maybe))
|
||||
(error "No Haskell session associated with this debug
|
||||
buffer. Please just close the buffer and start again.")))
|
||||
|
||||
(defun haskell-debug-buffer-name (session)
|
||||
"The debug buffer name for the current session."
|
||||
(format "*debug:%s*"
|
||||
(haskell-session-name session)))
|
||||
|
||||
(defun haskell-debug-get-breakpoints ()
|
||||
"Get the list of breakpoints currently set."
|
||||
(let ((string (haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
":show breaks")))
|
||||
(if (string= string "No active breakpoints.\n")
|
||||
(list)
|
||||
(mapcar #'haskell-debug-parse-break-point
|
||||
(haskell-debug-split-string string)))))
|
||||
|
||||
(defun haskell-debug-get-modules ()
|
||||
"Get the list of modules currently set."
|
||||
(let ((string (haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
":show modules")))
|
||||
(if (string= string "")
|
||||
(list)
|
||||
(mapcar #'haskell-debug-parse-module
|
||||
(haskell-debug-split-string string)))))
|
||||
|
||||
(defun haskell-debug-get-context ()
|
||||
"Get the current context."
|
||||
(let ((string (haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
":show context")))
|
||||
(if (string= string "")
|
||||
nil
|
||||
(haskell-debug-parse-context string))))
|
||||
|
||||
(defun haskell-debug-get-history ()
|
||||
"Get the step history."
|
||||
(let ((string (haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
":history")))
|
||||
(if (or (string= string "")
|
||||
(string= string "Not stopped at a breakpoint\n"))
|
||||
nil
|
||||
(if (string= string "Empty history. Perhaps you forgot to use :trace?\n")
|
||||
nil
|
||||
(let ((entries (mapcar #'haskell-debug-parse-history-entry
|
||||
(cl-remove-if (lambda (line) (or (string= "<end of history>" line)
|
||||
(string= "..." line)))
|
||||
(haskell-debug-split-string string)))))
|
||||
(setq haskell-debug-history-cache
|
||||
entries)
|
||||
entries)))))
|
||||
|
||||
(defun haskell-debug-insert-bindings (modules breakpoints context)
|
||||
"Insert a list of bindings."
|
||||
(if breakpoints
|
||||
(progn (haskell-debug-insert-binding "t" "trace an expression")
|
||||
(haskell-debug-insert-binding "s" "step into an expression")
|
||||
(haskell-debug-insert-binding "b" "breakpoint" t))
|
||||
(progn
|
||||
(when modules
|
||||
(haskell-debug-insert-binding "b" "breakpoint"))
|
||||
(when breakpoints
|
||||
(haskell-debug-insert-binding "s" "step into an expression" t))))
|
||||
(when breakpoints
|
||||
(haskell-debug-insert-binding "d" "delete breakpoint"))
|
||||
(when context
|
||||
(haskell-debug-insert-binding "a" "abandon context")
|
||||
(haskell-debug-insert-binding "c" "continue" t))
|
||||
(when context
|
||||
(haskell-debug-insert-binding "p" "previous step")
|
||||
(haskell-debug-insert-binding "n" "next step" t))
|
||||
(haskell-debug-insert-binding "g" "refresh" t)
|
||||
(insert "\n"))
|
||||
|
||||
(defun haskell-debug-insert-current-context (context history)
|
||||
"Insert the current context."
|
||||
(haskell-debug-insert-header "Context")
|
||||
(if context
|
||||
(haskell-debug-insert-context context history)
|
||||
(haskell-debug-insert-debug-finished))
|
||||
(insert "\n"))
|
||||
|
||||
(defun haskell-debug-insert-breakpoints (breakpoints)
|
||||
"insert the list of breakpoints."
|
||||
(haskell-debug-insert-header "Breakpoints")
|
||||
(if (null breakpoints)
|
||||
(haskell-debug-insert-muted "No active breakpoints.")
|
||||
(cl-loop for break in breakpoints
|
||||
do (insert (propertize (format "%d"
|
||||
(plist-get break :number))
|
||||
'face `((:weight bold))
|
||||
'break break)
|
||||
(haskell-debug-muted " - ")
|
||||
(propertize (plist-get break :module)
|
||||
'break break
|
||||
'break break)
|
||||
(haskell-debug-muted
|
||||
(format " (%d:%d)"
|
||||
(plist-get (plist-get break :span) :start-line)
|
||||
(plist-get (plist-get break :span) :start-col)))
|
||||
"\n")))
|
||||
(insert "\n"))
|
||||
|
||||
(defun haskell-debug-insert-modules (modules)
|
||||
"Insert the list of modules."
|
||||
(haskell-debug-insert-header "Modules")
|
||||
(if (null modules)
|
||||
(haskell-debug-insert-muted "No loaded modules.")
|
||||
(progn (cl-loop for module in modules
|
||||
do (insert (propertize (plist-get module :module)
|
||||
'module module
|
||||
'face `((:weight bold)))
|
||||
(haskell-debug-muted " - ")
|
||||
(propertize (file-name-nondirectory (plist-get module :path))
|
||||
'module module))
|
||||
do (insert "\n")))))
|
||||
|
||||
(defun haskell-debug-split-string (string)
|
||||
"Split GHCi's line-based output, stripping the trailing newline."
|
||||
(split-string string "\n" t))
|
||||
|
||||
(defun haskell-debug-parse-context (string)
|
||||
"Parse the context."
|
||||
(cond
|
||||
((string-match "^--> \\(.+\\)\n \\(.+\\)" string)
|
||||
(let ((name (match-string 1 string))
|
||||
(stopped (haskell-debug-parse-stopped-at (match-string 2 string))))
|
||||
(list :name name
|
||||
:path (plist-get stopped :path)
|
||||
:span (plist-get stopped :span))))))
|
||||
|
||||
(defun haskell-debug-insert-binding (binding desc &optional end)
|
||||
"Insert a helpful keybinding."
|
||||
(insert (propertize binding 'face 'haskell-debug-keybinding-face)
|
||||
(haskell-debug-muted " - ")
|
||||
desc
|
||||
(if end
|
||||
"\n"
|
||||
(haskell-debug-muted ", "))))
|
||||
|
||||
(defun haskell-debug-insert-header (title)
|
||||
"Insert a header title."
|
||||
(insert (propertize title
|
||||
'face 'haskell-debug-heading-face)
|
||||
"\n\n"))
|
||||
|
||||
(defun haskell-debug-insert-context (context history)
|
||||
"Insert the context and history."
|
||||
(when context
|
||||
(insert (propertize (plist-get context :name) 'face `((:weight bold)))
|
||||
(haskell-debug-muted " - ")
|
||||
(file-name-nondirectory (plist-get context :path))
|
||||
(haskell-debug-muted " (stopped)")
|
||||
"\n"))
|
||||
(when haskell-debug-bindings-cache
|
||||
(insert "\n")
|
||||
(let ((bindings haskell-debug-bindings-cache))
|
||||
(insert
|
||||
(haskell-debug-get-span-string
|
||||
(plist-get bindings :path)
|
||||
(plist-get bindings :span)))
|
||||
(insert "\n\n")
|
||||
(cl-loop for binding in (plist-get bindings :types)
|
||||
do (insert (haskell-fontify-as-mode binding 'haskell-mode)
|
||||
"\n"))))
|
||||
(let ((history (or history
|
||||
(list (haskell-debug-make-fake-history context)))))
|
||||
(when history
|
||||
(insert "\n")
|
||||
(haskell-debug-insert-history history))))
|
||||
|
||||
(defun haskell-debug-insert-debug-finished ()
|
||||
"Insert message that no debugging is happening, but if there is
|
||||
some old history, then display that."
|
||||
(if haskell-debug-history-cache
|
||||
(progn (haskell-debug-insert-muted "Finished debugging.")
|
||||
(insert "\n")
|
||||
(haskell-debug-insert-history haskell-debug-history-cache))
|
||||
(haskell-debug-insert-muted "Not debugging right now.")))
|
||||
|
||||
(defun haskell-debug-insert-muted (text)
|
||||
"Insert some muted text."
|
||||
(insert (haskell-debug-muted text)
|
||||
"\n"))
|
||||
|
||||
(defun haskell-debug-muted (text)
|
||||
"Make some muted text."
|
||||
(propertize text 'face 'haskell-debug-muted-face))
|
||||
|
||||
(defun haskell-debug-parse-logged (string)
|
||||
"Parse the logged breakpoint."
|
||||
(cond
|
||||
((string= "no more logged breakpoints\n" string)
|
||||
nil)
|
||||
((string= "already at the beginning of the history\n" string)
|
||||
nil)
|
||||
(t
|
||||
(with-temp-buffer
|
||||
(insert string)
|
||||
(goto-char (point-min))
|
||||
(list :path (progn (search-forward " at ")
|
||||
(buffer-substring-no-properties
|
||||
(point)
|
||||
(1- (search-forward ":"))))
|
||||
:span (haskell-debug-parse-span
|
||||
(buffer-substring-no-properties
|
||||
(point)
|
||||
(line-end-position)))
|
||||
:types (progn (forward-line)
|
||||
(haskell-debug-split-string
|
||||
(buffer-substring-no-properties
|
||||
(point)
|
||||
(point-max)))))))))
|
||||
|
||||
(defun haskell-debug-parse-stopped-at (string)
|
||||
"Parse the location stopped at from the given string.
|
||||
|
||||
For example:
|
||||
|
||||
Stopped at /home/foo/project/src/x.hs:6:25-36
|
||||
|
||||
"
|
||||
(let ((index (string-match "Stopped at \\([^:]+\\):\\(.+\\)\n?"
|
||||
string)))
|
||||
(when index
|
||||
(list :path (match-string 1 string)
|
||||
:span (haskell-debug-parse-span (match-string 2 string))
|
||||
:types (cdr (haskell-debug-split-string (substring string index)))))))
|
||||
|
||||
(defun haskell-debug-get-span-string (path span)
|
||||
"Get the string from the PATH and the SPAN."
|
||||
(save-window-excursion
|
||||
(find-file path)
|
||||
(buffer-substring
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :start-line)))
|
||||
(forward-char (1- (plist-get span :start-col)))
|
||||
(point))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :end-line)))
|
||||
(forward-char (plist-get span :end-col))
|
||||
(point)))))
|
||||
|
||||
(defun haskell-debug-make-fake-history (context)
|
||||
"Make a fake history item."
|
||||
(list :index -1
|
||||
:path (plist-get context :path)
|
||||
:span (plist-get context :span)))
|
||||
|
||||
(defun haskell-debug-insert-history (history)
|
||||
"Insert tracing HISTORY."
|
||||
(let ((i (length history)))
|
||||
(cl-loop for span in history
|
||||
do (let ((string (haskell-debug-get-span-string
|
||||
(plist-get span :path)
|
||||
(plist-get span :span))))
|
||||
(insert (propertize (format "%4d" i)
|
||||
'face 'haskell-debug-trace-number-face)
|
||||
" "
|
||||
(haskell-debug-preview-span
|
||||
(plist-get span :span)
|
||||
string
|
||||
t)
|
||||
"\n")
|
||||
(setq i (1- i))))))
|
||||
|
||||
(defun haskell-debug-parse-span (string)
|
||||
"Parse a source span from a string.
|
||||
|
||||
Examples:
|
||||
|
||||
(5,1)-(6,37)
|
||||
6:25-36
|
||||
5:20
|
||||
|
||||
People like to make other people's lives interesting by making
|
||||
variances in source span notation."
|
||||
(cond
|
||||
((string-match "\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)"
|
||||
string)
|
||||
(list :start-line (string-to-number (match-string 1 string))
|
||||
:start-col (string-to-number (match-string 2 string))
|
||||
:end-line (string-to-number (match-string 1 string))
|
||||
:end-col (string-to-number (match-string 3 string))))
|
||||
((string-match "\\([0-9]+\\):\\([0-9]+\\)"
|
||||
string)
|
||||
(list :start-line (string-to-number (match-string 1 string))
|
||||
:start-col (string-to-number (match-string 2 string))
|
||||
:end-line (string-to-number (match-string 1 string))
|
||||
:end-col (string-to-number (match-string 2 string))))
|
||||
((string-match "(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))"
|
||||
string)
|
||||
(list :start-line (string-to-number (match-string 1 string))
|
||||
:start-col (string-to-number (match-string 2 string))
|
||||
:end-line (string-to-number (match-string 3 string))
|
||||
:end-col (string-to-number (match-string 4 string))))
|
||||
(t (error "Unable to parse source span from string: %s"
|
||||
string))))
|
||||
|
||||
(defun haskell-debug-preview-span (span string &optional collapsed)
|
||||
"Make a one-line preview of the given expression."
|
||||
(with-temp-buffer
|
||||
(haskell-mode)
|
||||
(insert string)
|
||||
(when (/= 0 (plist-get span :start-col))
|
||||
(indent-rigidly (point-min)
|
||||
(point-max)
|
||||
1))
|
||||
(if (fboundp 'font-lock-ensure)
|
||||
(font-lock-ensure)
|
||||
(with-no-warnings (font-lock-fontify-buffer)))
|
||||
(when (/= 0 (plist-get span :start-col))
|
||||
(indent-rigidly (point-min)
|
||||
(point-max)
|
||||
-1))
|
||||
(goto-char (point-min))
|
||||
(if collapsed
|
||||
(replace-regexp-in-string
|
||||
"\n[ ]*"
|
||||
(propertize " " 'face 'haskell-debug-newline-face)
|
||||
(buffer-substring (point-min)
|
||||
(point-max)))
|
||||
(buffer-string))))
|
||||
|
||||
(defun haskell-debug-start (session)
|
||||
"Start the debug mode."
|
||||
(setq buffer-read-only t)
|
||||
(haskell-session-assign session)
|
||||
(haskell-debug/refresh))
|
||||
|
||||
(defun haskell-debug ()
|
||||
"Start the debugger for the current Haskell (GHCi) session."
|
||||
(interactive)
|
||||
(let ((session (haskell-debug-session)))
|
||||
(switch-to-buffer-other-window (haskell-debug-buffer-name session))
|
||||
(unless (eq major-mode 'haskell-debug-mode)
|
||||
(haskell-debug-mode)
|
||||
(haskell-debug-start session))))
|
||||
|
||||
(defun haskell-debug-break (break)
|
||||
"Set BREAK breakpoint in module at line/col."
|
||||
(haskell-process-queue-without-filters
|
||||
(haskell-debug-process)
|
||||
(format ":break %s %s %d"
|
||||
(plist-get break :module)
|
||||
(plist-get (plist-get break :span) :start-line)
|
||||
(plist-get (plist-get break :span) :start-col))))
|
||||
|
||||
(defun haskell-debug-navigate (direction)
|
||||
"Navigate in DIRECTION \"back\" or \"forward\"."
|
||||
(let ((string (haskell-process-queue-sync-request
|
||||
(haskell-debug-process)
|
||||
(concat ":" direction))))
|
||||
(let ((bindings (haskell-debug-parse-logged string)))
|
||||
(setq haskell-debug-bindings-cache
|
||||
bindings)
|
||||
(when (not bindings)
|
||||
(message "No more %s results!" direction)))
|
||||
(haskell-debug/refresh)))
|
||||
|
||||
(defun haskell-debug-session-debugging-p (session)
|
||||
"Does the session have a debugging buffer open?"
|
||||
(not (not (get-buffer (haskell-debug-buffer-name session)))))
|
||||
|
||||
(defun haskell-debug-highlight (path &optional span)
|
||||
"Highlight the file at span."
|
||||
(let ((p (make-overlay
|
||||
(line-beginning-position)
|
||||
(line-end-position))))
|
||||
(overlay-put p 'face `((:background "#eee")))
|
||||
(with-current-buffer
|
||||
(if span
|
||||
(save-window-excursion
|
||||
(find-file path)
|
||||
(current-buffer))
|
||||
(find-file path)
|
||||
(current-buffer))
|
||||
(let ((o (when span
|
||||
(make-overlay
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :start-line)))
|
||||
(forward-char (1- (plist-get span :start-col)))
|
||||
(point))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (plist-get span :end-line)))
|
||||
(forward-char (plist-get span :end-col))
|
||||
(point))))))
|
||||
(when o
|
||||
(overlay-put o 'face `((:background "#eee"))))
|
||||
(sit-for 0.5)
|
||||
(when o
|
||||
(delete-overlay o))
|
||||
(delete-overlay p)))))
|
||||
|
||||
(defun haskell-debug-parse-history-entry (string)
|
||||
"Parse a history entry."
|
||||
(if (string-match "^\\([-0-9]+\\)[ ]+:[ ]+\\([A-Za-z0-9_':]+\\)[ ]+(\\([^:]+\\):\\(.+?\\))$"
|
||||
string)
|
||||
(list :index (string-to-number (match-string 1 string))
|
||||
:name (match-string 2 string)
|
||||
:path (match-string 3 string)
|
||||
:span (haskell-debug-parse-span (match-string 4 string)))
|
||||
(error "Unable to parse history entry: %s" string)))
|
||||
|
||||
(defun haskell-debug-parse-module (string)
|
||||
"Parse a module and path.
|
||||
|
||||
For example:
|
||||
|
||||
X ( /home/foo/X.hs, interpreted )
|
||||
Main ( /home/foo/X.hs, /home/foo/X.o )
|
||||
"
|
||||
(if (string-match "\\([^ ]+\\)[ ]+( \\([^ ]+?\\), [/a-zA-Z0-9\.]+ )$"
|
||||
string)
|
||||
(list :module (match-string 1 string)
|
||||
:path (match-string 2 string))
|
||||
(error "Unable to parse module from string: %s"
|
||||
string)))
|
||||
|
||||
(defun haskell-debug-parse-break-point (string)
|
||||
"Parse a breakpoint number, module and location from a string.
|
||||
|
||||
For example:
|
||||
|
||||
[13] Main /home/foo/src/x.hs:(5,1)-(6,37)
|
||||
|
||||
"
|
||||
(if (string-match "^\\[\\([0-9]+\\)\\] \\([^ ]+\\) \\([^:]+\\):\\(.+\\)$"
|
||||
string)
|
||||
(list :number (string-to-number (match-string 1 string))
|
||||
:module (match-string 2 string)
|
||||
:path (match-string 3 string)
|
||||
:span (haskell-debug-parse-span (match-string 4 string)))
|
||||
(error "Unable to parse breakpoint from string: %s"
|
||||
string)))
|
||||
|
||||
(provide 'haskell-debug)
|
||||
|
||||
;;; haskell-debug.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-debug.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-debug.elc
Normal file
Binary file not shown.
687
elpa/haskell-mode-20171022.26/haskell-decl-scan.el
Normal file
687
elpa/haskell-mode-20171022.26/haskell-decl-scan.el
Normal file
@@ -0,0 +1,687 @@
|
||||
;;; haskell-decl-scan.el --- Declaration scanning module for Haskell Mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1997-1998 Graeme E Moss
|
||||
;; Copyright (C) 2016 Chris Gregory
|
||||
|
||||
;; Author: 1997-1998 Graeme E Moss <gem@cs.york.ac.uk>
|
||||
;; Maintainer: Stefan Monnier <monnier@gnu.org>
|
||||
;; Keywords: declarations menu files Haskell
|
||||
;; URL: http://cvs.haskell.org/cgi-bin/cvsweb.cgi/fptools/CONTRIB/haskell-modes/emacs/haskell-decl-scan.el?rev=HEAD
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Purpose:
|
||||
;;
|
||||
;; Top-level declarations are scanned and placed in a menu. Supports
|
||||
;; full Latin1 Haskell 1.4 as well as literate scripts.
|
||||
;;
|
||||
;;
|
||||
;; Installation:
|
||||
;;
|
||||
;; To turn declaration scanning on for all Haskell buffers under the
|
||||
;; Haskell mode of Moss&Thorn, add this to .emacs:
|
||||
;;
|
||||
;; (add-hook 'haskell-mode-hook 'haskell-decl-scan-mode)
|
||||
;;
|
||||
;; Otherwise, call `haskell-decl-scan-mode'.
|
||||
;;
|
||||
;;
|
||||
;; Customisation:
|
||||
;;
|
||||
;; M-x customize-group haskell-decl-scan
|
||||
;;
|
||||
;;
|
||||
;; History:
|
||||
;;
|
||||
;; If you have any problems or suggestions, after consulting the list
|
||||
;; below, email gem@cs.york.ac.uk quoting the version of the library
|
||||
;; you are using, the version of Emacs you are using, and a small
|
||||
;; example of the problem or suggestion. Note that this library
|
||||
;; requires a reasonably recent version of Emacs.
|
||||
;;
|
||||
;; Uses `imenu' under Emacs.
|
||||
;;
|
||||
;; Version 1.2:
|
||||
;; Added support for LaTeX-style literate scripts.
|
||||
;;
|
||||
;; Version 1.1:
|
||||
;; Use own syntax table. Fixed bug for very small buffers. Use
|
||||
;; markers instead of pointers (markers move with the text).
|
||||
;;
|
||||
;; Version 1.0:
|
||||
;; Brought over from Haskell mode v1.1.
|
||||
;;
|
||||
;;
|
||||
;; Present Limitations/Future Work (contributions are most welcome!):
|
||||
;;
|
||||
;; . Declarations requiring information extending beyond starting line
|
||||
;; don't get scanned properly, eg.
|
||||
;; > class Eq a =>
|
||||
;; > Test a
|
||||
;;
|
||||
;; . Comments placed in the midst of the first few lexemes of a
|
||||
;; declaration will cause havoc, eg.
|
||||
;; > infixWithComments :: Int -> Int -> Int
|
||||
;; > x {-nastyComment-} `infixWithComments` y = x + y
|
||||
;; but are not worth worrying about.
|
||||
;;
|
||||
;; . Would be nice to scan other top-level declarations such as
|
||||
;; methods of a class, datatype field labels... any more?
|
||||
;;
|
||||
;; . Support for GreenCard?
|
||||
;;
|
||||
;; . Re-running (literate-)haskell-imenu should not cause the problems
|
||||
;; that it does. The ability to turn off scanning would also be
|
||||
;; useful. (Note that re-running (literate-)haskell-mode seems to
|
||||
;; cause no problems.)
|
||||
|
||||
;; All functions/variables start with
|
||||
;; `(turn-(on/off)-)haskell-decl-scan' or `haskell-ds-'.
|
||||
|
||||
;; The imenu support is based on code taken from `hugs-mode',
|
||||
;; thanks go to Chris Van Humbeeck.
|
||||
|
||||
;; Version.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-mode)
|
||||
(require 'syntax)
|
||||
(require 'imenu)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell-decl-scan nil
|
||||
"Haskell declaration scanning (`imenu' support)."
|
||||
:link '(custom-manual "(haskell-mode)haskell-decl-scan-mode")
|
||||
:group 'haskell
|
||||
:prefix "haskell-decl-scan-")
|
||||
|
||||
(defcustom haskell-decl-scan-bindings-as-variables nil
|
||||
"Whether to put top-level value bindings into a \"Variables\" category."
|
||||
:group 'haskell-decl-scan
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom haskell-decl-scan-add-to-menubar t
|
||||
"Whether to add a \"Declarations\" menu entry to menu bar."
|
||||
:group 'haskell-decl-scan
|
||||
:type 'boolean)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; General declaration scanning functions.
|
||||
|
||||
(defvar haskell-ds-start-keywords-re
|
||||
(concat "\\(\\<"
|
||||
"class\\|data\\|i\\(mport\\|n\\(fix\\(\\|[lr]\\)\\|stance\\)\\)\\|"
|
||||
"module\\|primitive\\|type\\|newtype"
|
||||
"\\)\\>")
|
||||
"Keywords that may start a declaration.")
|
||||
|
||||
(defvar haskell-ds-syntax-table
|
||||
(let ((table (copy-syntax-table haskell-mode-syntax-table)))
|
||||
(modify-syntax-entry ?\' "w" table)
|
||||
(modify-syntax-entry ?_ "w" table)
|
||||
(modify-syntax-entry ?\\ "_" table)
|
||||
table)
|
||||
"Syntax table used for Haskell declaration scanning.")
|
||||
|
||||
|
||||
(defun haskell-ds-get-variable (prefix)
|
||||
"Return variable involved in value binding or type signature.
|
||||
Assumes point is looking at the regexp PREFIX followed by the
|
||||
start of a declaration (perhaps in the middle of a series of
|
||||
declarations concerning a single variable). Otherwise return nil.
|
||||
Point is not changed."
|
||||
;; I think I can now handle all declarations bar those with comments
|
||||
;; nested before the second lexeme.
|
||||
(save-excursion
|
||||
(with-syntax-table haskell-ds-syntax-table
|
||||
(if (looking-at prefix) (goto-char (match-end 0)))
|
||||
;; Keyword.
|
||||
(if (looking-at haskell-ds-start-keywords-re)
|
||||
nil
|
||||
(or ;; Parenthesized symbolic variable.
|
||||
(and (looking-at "(\\(\\s_+\\))") (match-string-no-properties 1))
|
||||
;; General case.
|
||||
(if (looking-at
|
||||
(if (eq ?\( (char-after))
|
||||
;; Skip paranthesised expression.
|
||||
(progn
|
||||
(forward-sexp)
|
||||
;; Repeating this code and avoiding moving point if
|
||||
;; possible speeds things up.
|
||||
"\\(\\'\\)?\\s-*\\(\\s_+\\|`\\(\\sw+\\)`\\)")
|
||||
"\\(\\sw+\\)?\\s-*\\(\\s_+\\|`\\(\\sw+\\)`\\)"))
|
||||
(let ((match2 (match-string-no-properties 2)))
|
||||
;; Weed out `::', `∷',`=' and `|' from potential infix
|
||||
;; symbolic variable.
|
||||
(if (member match2 '("::" "∷" "=" "|"))
|
||||
;; Variable identifier.
|
||||
(match-string-no-properties 1)
|
||||
(if (eq (aref match2 0) ?\`)
|
||||
;; Infix variable identifier.
|
||||
(match-string-no-properties 3)
|
||||
;; Infix symbolic variable.
|
||||
match2))))
|
||||
;; Variable identifier.
|
||||
(and (looking-at "\\sw+") (match-string-no-properties 0)))))))
|
||||
|
||||
(defun haskell-ds-move-to-start-regexp (inc regexp)
|
||||
"Move to beginning of line that succeeds/precedes (INC = 1/-1)
|
||||
current line that starts with REGEXP and is not in `font-lock-comment-face'."
|
||||
;; Making this defsubst instead of defun appears to have little or
|
||||
;; no effect on efficiency. It is probably not called enough to do
|
||||
;; so.
|
||||
(while (and (= (forward-line inc) 0)
|
||||
(or (not (looking-at regexp))
|
||||
(eq (get-text-property (point) 'face)
|
||||
'font-lock-comment-face)))))
|
||||
|
||||
(defun haskell-ds-move-to-start-regexp-skipping-comments (inc regexp)
|
||||
"Like haskell-ds-move-to-start-regexp, but uses syntax-ppss to
|
||||
skip comments"
|
||||
(let (p)
|
||||
(cl-loop
|
||||
do (setq p (point))
|
||||
(haskell-ds-move-to-start-regexp inc regexp)
|
||||
while (and (nth 4 (syntax-ppss))
|
||||
(/= p (point))))))
|
||||
|
||||
(defvar literate-haskell-ds-line-prefix "> ?"
|
||||
"Regexp matching start of a line of Bird-style literate code.
|
||||
Current value is \"> \" as we assume top-level declarations start
|
||||
at column 3. Must not contain the special \"^\" regexp as we may
|
||||
not use the regexp at the start of a regexp string. Note this is
|
||||
only for `imenu' support.")
|
||||
|
||||
(defvar haskell-ds-start-decl-re "\\(\\sw\\|(\\)"
|
||||
"The regexp that starts a Haskell declaration.")
|
||||
|
||||
(defvar literate-haskell-ds-start-decl-re
|
||||
(concat literate-haskell-ds-line-prefix haskell-ds-start-decl-re)
|
||||
"The regexp that starts a Bird-style literate Haskell declaration.")
|
||||
|
||||
(defun haskell-ds-whitespace-p (char)
|
||||
"Test if CHAR is a whitespace character."
|
||||
;; the nil is a bob/eob test
|
||||
(member char '(nil ?\t ?\n ?\ )))
|
||||
|
||||
(defun haskell-ds-move-to-decl (direction bird-literate fix)
|
||||
"General function for moving to the start of a declaration,
|
||||
either forwards or backwards from point, with normal or with Bird-style
|
||||
literate scripts. If DIRECTION is t, then forward, else backward. If
|
||||
BIRD-LITERATE is t, then treat as Bird-style literate scripts, else
|
||||
normal scripts. Returns point if point is left at the start of a
|
||||
declaration, and nil otherwise, ie. because point is at the beginning
|
||||
or end of the buffer and no declaration starts there. If FIX is t,
|
||||
then point does not move if already at the start of a declaration."
|
||||
;; As `haskell-ds-get-variable' cannot separate an infix variable
|
||||
;; identifier out of a value binding with non-alphanumeric first
|
||||
;; argument, this function will treat such value bindings as
|
||||
;; separate from the declarations surrounding it.
|
||||
(let ( ;; The variable typed or bound in the current series of
|
||||
;; declarations.
|
||||
name
|
||||
;; The variable typed or bound in the new declaration.
|
||||
newname
|
||||
;; Hack to solve hard problem for Bird-style literate scripts
|
||||
;; that start with a declaration. We are in the abyss if
|
||||
;; point is before start of this declaration.
|
||||
abyss
|
||||
(line-prefix (if bird-literate literate-haskell-ds-line-prefix ""))
|
||||
;; The regexp to match for the start of a declaration.
|
||||
(start-decl-re (if bird-literate
|
||||
literate-haskell-ds-start-decl-re
|
||||
haskell-ds-start-decl-re))
|
||||
(increment (if direction 1 -1))
|
||||
(bound (if direction (point-max) (point-min))))
|
||||
;; Change syntax table.
|
||||
(with-syntax-table haskell-ds-syntax-table
|
||||
;; move to beginning of line that starts the "current
|
||||
;; declaration" (dependent on DIRECTION and FIX), and then get
|
||||
;; the variable typed or bound by this declaration, if any.
|
||||
(let ( ;; Where point was at call of function.
|
||||
(here (point))
|
||||
;; Where the declaration on this line (if any) starts.
|
||||
(start (progn
|
||||
(beginning-of-line)
|
||||
;; Checking the face to ensure a declaration starts
|
||||
;; here seems to be the only addition to make this
|
||||
;; module support LaTeX-style literate scripts.
|
||||
(if (and (looking-at start-decl-re)
|
||||
(not (elt (syntax-ppss) 4)))
|
||||
(match-beginning 1)))))
|
||||
(if (and start
|
||||
;; This complicated boolean determines whether we
|
||||
;; should include the declaration that starts on the
|
||||
;; current line as the "current declaration" or not.
|
||||
(or (and (or (and direction (not fix))
|
||||
(and (not direction) fix))
|
||||
(>= here start))
|
||||
(and (or (and direction fix)
|
||||
(and (not direction) (not fix)))
|
||||
(> here start))))
|
||||
;; If so, we are already at start of the current line, so
|
||||
;; do nothing.
|
||||
()
|
||||
;; If point was before start of a declaration on the first
|
||||
;; line of the buffer (possible for Bird-style literate
|
||||
;; scripts) then we are in the abyss.
|
||||
(if (and start (bobp))
|
||||
(setq abyss t)
|
||||
;; Otherwise we move to the start of the first declaration
|
||||
;; on a line preceding the current one, skipping comments
|
||||
(haskell-ds-move-to-start-regexp-skipping-comments -1 start-decl-re))))
|
||||
;; If we are in the abyss, position and return as appropriate.
|
||||
(if abyss
|
||||
(if (not direction)
|
||||
nil
|
||||
(re-search-forward (concat "\\=" line-prefix) nil t)
|
||||
(point))
|
||||
;; Get the variable typed or bound by this declaration, if any.
|
||||
(setq name (haskell-ds-get-variable line-prefix))
|
||||
(if (not name)
|
||||
;; If no such variable, stop at the start of this
|
||||
;; declaration if moving backward, or move to the next
|
||||
;; declaration if moving forward.
|
||||
(if direction
|
||||
(haskell-ds-move-to-start-regexp-skipping-comments 1 start-decl-re))
|
||||
;; If there is a variable, find the first
|
||||
;; succeeding/preceding declaration that does not type or
|
||||
;; bind it. Check for reaching start/end of buffer and
|
||||
;; comments.
|
||||
(haskell-ds-move-to-start-regexp-skipping-comments increment start-decl-re)
|
||||
(while (and (/= (point) bound)
|
||||
(and (setq newname (haskell-ds-get-variable line-prefix))
|
||||
(string= name newname)))
|
||||
(setq name newname)
|
||||
(haskell-ds-move-to-start-regexp-skipping-comments increment start-decl-re))
|
||||
;; If we are going backward, and have either reached a new
|
||||
;; declaration or the beginning of a buffer that does not
|
||||
;; start with a declaration, move forward to start of next
|
||||
;; declaration (which must exist). Otherwise, we are done.
|
||||
(if (and (not direction)
|
||||
(or (and (looking-at start-decl-re)
|
||||
(not (string= name
|
||||
;; Note we must not use
|
||||
;; newname here as this may
|
||||
;; not have been set if we
|
||||
;; have reached the beginning
|
||||
;; of the buffer.
|
||||
(haskell-ds-get-variable
|
||||
line-prefix))))
|
||||
(and (not (looking-at start-decl-re))
|
||||
(bobp))))
|
||||
(haskell-ds-move-to-start-regexp-skipping-comments 1 start-decl-re)))
|
||||
;; Store whether we are at the start of a declaration or not.
|
||||
;; Used to calculate final result.
|
||||
(let ((at-start-decl (looking-at start-decl-re)))
|
||||
;; If we are at the beginning of a line, move over
|
||||
;; line-prefix, if present at point.
|
||||
(if (bolp)
|
||||
(re-search-forward (concat "\\=" line-prefix) (point-max) t))
|
||||
;; Return point if at the start of a declaration and nil
|
||||
;; otherwise.
|
||||
(if at-start-decl (point) nil))))))
|
||||
|
||||
(defun haskell-ds-bird-p ()
|
||||
(and (boundp 'haskell-literate) (eq haskell-literate 'bird)))
|
||||
|
||||
(defun haskell-ds-backward-decl ()
|
||||
"Move backward to the first character that starts a top-level declaration.
|
||||
A series of declarations concerning one variable is treated as one
|
||||
declaration by this function. So, if point is within a top-level
|
||||
declaration then move it to the start of that declaration. If point
|
||||
is already at the start of a top-level declaration, then move it to
|
||||
the start of the preceding declaration. Returns point if point is
|
||||
left at the start of a declaration, and nil otherwise, because
|
||||
point is at the beginning of the buffer and no declaration starts
|
||||
there."
|
||||
(interactive)
|
||||
(haskell-ds-move-to-decl nil (haskell-ds-bird-p) nil))
|
||||
|
||||
(defun haskell-ds-comment-p
|
||||
(&optional
|
||||
pt)
|
||||
"Test if the cursor is on whitespace or a comment.
|
||||
|
||||
`PT' defaults to `(point)'"
|
||||
;; ensure result is `t' or `nil' instead of just truthy
|
||||
(if (or
|
||||
;; is cursor on whitespace
|
||||
(haskell-ds-whitespace-p (following-char))
|
||||
;; http://emacs.stackexchange.com/questions/14269/how-to-detect-if-the-point-is-within-a-comment-area
|
||||
;; is cursor at begging, inside, or end of comment
|
||||
(let ((fontfaces (get-text-property (or pt
|
||||
(point)) 'face)))
|
||||
(when (not (listp fontfaces))
|
||||
(setf fontfaces (list fontfaces)))
|
||||
(delq nil (mapcar
|
||||
#'(lambda (f)
|
||||
(member f '(font-lock-comment-face
|
||||
font-lock-doc-face
|
||||
font-lock-comment-delimiter-face)))
|
||||
fontfaces))))
|
||||
t
|
||||
nil))
|
||||
|
||||
(defun haskell-ds-line-commented-p ()
|
||||
"Tests if all characters from `point' to `end-of-line' pass
|
||||
`haskell-ds-comment-p'"
|
||||
(let ((r t))
|
||||
(while (and r (not (eolp)))
|
||||
(if (not (haskell-ds-comment-p))
|
||||
(setq r nil))
|
||||
(forward-char))
|
||||
r))
|
||||
|
||||
(defun haskell-ds-forward-decl ()
|
||||
"Move forward to the first character that starts a top-level
|
||||
declaration. As `haskell-ds-backward-decl' but forward."
|
||||
(interactive)
|
||||
(let ((p (point)) b e empty was-at-bob)
|
||||
;; Go back to beginning of defun, then go to beginning of next
|
||||
(haskell-ds-move-to-decl nil (haskell-ds-bird-p) nil)
|
||||
(setq b (point))
|
||||
(haskell-ds-move-to-decl t (haskell-ds-bird-p) nil)
|
||||
(setq e (point))
|
||||
;; tests if line is empty
|
||||
(setq empty (and (<= (point) p)
|
||||
(not (eolp))))
|
||||
(setq was-at-bob (and (= (point-min) b)
|
||||
(= b p)
|
||||
(< p e)))
|
||||
;; this conditional allows for when empty lines at end, first
|
||||
;; `C-M-e' will go to end of defun, next will go to end of file.
|
||||
(when (or was-at-bob
|
||||
empty)
|
||||
(if (or (and was-at-bob
|
||||
(= ?\n
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(following-char))))
|
||||
empty)
|
||||
(haskell-ds-move-to-decl t (haskell-ds-bird-p) nil))
|
||||
;; Then go back to end of current
|
||||
(forward-line -1)
|
||||
(while (and (haskell-ds-line-commented-p)
|
||||
;; prevent infinite loop
|
||||
(not (bobp)))
|
||||
(forward-line -1))
|
||||
(forward-line 1)))
|
||||
(point))
|
||||
|
||||
(defun haskell-ds-generic-find-next-decl (bird-literate)
|
||||
"Find the name, position and type of the declaration at or after point.
|
||||
Return ((NAME . (START-POSITION . NAME-POSITION)) . TYPE)
|
||||
if one exists and nil otherwise. The start-position is at the start
|
||||
of the declaration, and the name-position is at the start of the name
|
||||
of the declaration. The name is a string, the positions are buffer
|
||||
positions and the type is one of the symbols \"variable\", \"datatype\",
|
||||
\"class\", \"import\" and \"instance\"."
|
||||
(let ( ;; The name, type and name-position of the declaration to
|
||||
;; return.
|
||||
name
|
||||
type
|
||||
name-pos
|
||||
;; Buffer positions marking the start and end of the space
|
||||
;; containing a declaration.
|
||||
start
|
||||
end)
|
||||
;; Change to declaration scanning syntax.
|
||||
(with-syntax-table haskell-ds-syntax-table
|
||||
;; Stop when we are at the end of the buffer or when a valid
|
||||
;; declaration is grabbed.
|
||||
(while (not (or (eobp) name))
|
||||
;; Move forward to next declaration at or after point.
|
||||
(haskell-ds-move-to-decl t bird-literate t)
|
||||
;; Start and end of search space is currently just the starting
|
||||
;; line of the declaration.
|
||||
(setq start (point)
|
||||
end (line-end-position))
|
||||
(cond
|
||||
;; If the start of the top-level declaration does not begin
|
||||
;; with a starting keyword, then (if legal) must be a type
|
||||
;; signature or value binding, and the variable concerned is
|
||||
;; grabbed.
|
||||
((not (looking-at haskell-ds-start-keywords-re))
|
||||
(setq name (haskell-ds-get-variable ""))
|
||||
(if name
|
||||
(progn
|
||||
(setq type 'variable)
|
||||
(re-search-forward (regexp-quote name) end t)
|
||||
(setq name-pos (match-beginning 0)))))
|
||||
;; User-defined datatype declaration.
|
||||
((re-search-forward "\\=\\(data\\|newtype\\|type\\)\\>" end t)
|
||||
(re-search-forward "=>" end t)
|
||||
(if (looking-at "[ \t]*\\(\\sw+\\)")
|
||||
(progn
|
||||
(setq name (match-string-no-properties 1))
|
||||
(setq name-pos (match-beginning 1))
|
||||
(setq type 'datatype))))
|
||||
;; Class declaration.
|
||||
((re-search-forward "\\=class\\>" end t)
|
||||
(re-search-forward "=>" end t)
|
||||
(if (looking-at "[ \t]*\\(\\sw+\\)")
|
||||
(progn
|
||||
(setq name (match-string-no-properties 1))
|
||||
(setq name-pos (match-beginning 1))
|
||||
(setq type 'class))))
|
||||
;; Import declaration.
|
||||
((looking-at "import[ \t]+\\(?:safe[\t ]+\\)?\\(?:qualified[ \t]+\\)?\\(?:\"[^\"]*\"[\t ]+\\)?\\(\\(?:\\sw\\|.\\)+\\)")
|
||||
(setq name (match-string-no-properties 1))
|
||||
(setq name-pos (match-beginning 1))
|
||||
(setq type 'import))
|
||||
;; Instance declaration.
|
||||
((re-search-forward "\\=instance[ \t]+" end t)
|
||||
(re-search-forward "=>[ \t]+" end t)
|
||||
;; The instance "title" starts just after the `instance' (and
|
||||
;; any context) and finishes just before the _first_ `where'
|
||||
;; if one exists. This solution is ugly, but I can't find a
|
||||
;; nicer one---a simple regexp will pick up the last `where',
|
||||
;; which may be rare but nevertheless...
|
||||
(setq name-pos (point))
|
||||
(setq name (buffer-substring-no-properties
|
||||
(point)
|
||||
(progn
|
||||
;; Look for a `where'.
|
||||
(if (re-search-forward "\\<where\\>" end t)
|
||||
;; Move back to just before the `where'.
|
||||
(progn
|
||||
(re-search-backward "\\s-where")
|
||||
(point))
|
||||
;; No `where' so move to last non-whitespace
|
||||
;; before `end'.
|
||||
(progn
|
||||
(goto-char end)
|
||||
(skip-chars-backward " \t")
|
||||
(point))))))
|
||||
;; If we did not manage to extract a name, cancel this
|
||||
;; declaration (eg. when line ends in "=> ").
|
||||
(if (string-match "^[ \t]*$" name) (setq name nil))
|
||||
(setq type 'instance)))
|
||||
;; Move past start of current declaration.
|
||||
(goto-char end))
|
||||
;; If we have a valid declaration then return it, otherwise return
|
||||
;; nil.
|
||||
(if name
|
||||
(cons (cons name (cons (copy-marker start t) (copy-marker name-pos t)))
|
||||
type)
|
||||
nil))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Declaration scanning via `imenu'.
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-ds-create-imenu-index ()
|
||||
"Function for finding `imenu' declarations in Haskell mode.
|
||||
Finds all declarations (classes, variables, imports, instances and
|
||||
datatypes) in a Haskell file for the `imenu' package."
|
||||
;; Each list has elements of the form `(INDEX-NAME . INDEX-POSITION)'.
|
||||
;; These lists are nested using `(INDEX-TITLE . INDEX-ALIST)'.
|
||||
(let* ((bird-literate (haskell-ds-bird-p))
|
||||
(index-alist '())
|
||||
(index-class-alist '()) ;; Classes
|
||||
(index-var-alist '()) ;; Variables
|
||||
(index-imp-alist '()) ;; Imports
|
||||
(index-inst-alist '()) ;; Instances
|
||||
(index-type-alist '()) ;; Datatypes
|
||||
;; Variables for showing progress.
|
||||
(bufname (buffer-name))
|
||||
(divisor-of-progress (max 1 (/ (buffer-size) 100)))
|
||||
;; The result we wish to return.
|
||||
result)
|
||||
(goto-char (point-min))
|
||||
;; Loop forwards from the beginning of the buffer through the
|
||||
;; starts of the top-level declarations.
|
||||
(while (< (point) (point-max))
|
||||
(message "Scanning declarations in %s... (%3d%%)" bufname
|
||||
(/ (- (point) (point-min)) divisor-of-progress))
|
||||
;; Grab the next declaration.
|
||||
(setq result (haskell-ds-generic-find-next-decl bird-literate))
|
||||
(if result
|
||||
;; If valid, extract the components of the result.
|
||||
(let* ((name-posns (car result))
|
||||
(name (car name-posns))
|
||||
(posns (cdr name-posns))
|
||||
(start-pos (car posns))
|
||||
(type (cdr result)))
|
||||
;; Place `(name . start-pos)' in the correct alist.
|
||||
(cl-case type
|
||||
(variable
|
||||
(setq index-var-alist
|
||||
(cl-acons name start-pos index-var-alist)))
|
||||
(datatype
|
||||
(setq index-type-alist
|
||||
(cl-acons name start-pos index-type-alist)))
|
||||
(class
|
||||
(setq index-class-alist
|
||||
(cl-acons name start-pos index-class-alist)))
|
||||
(import
|
||||
(setq index-imp-alist
|
||||
(cl-acons name start-pos index-imp-alist)))
|
||||
(instance
|
||||
(setq index-inst-alist
|
||||
(cl-acons name start-pos index-inst-alist)))))))
|
||||
;; Now sort all the lists, label them, and place them in one list.
|
||||
(message "Sorting declarations in %s..." bufname)
|
||||
(when index-type-alist
|
||||
(push (cons "Datatypes"
|
||||
(sort index-type-alist 'haskell-ds-imenu-label-cmp))
|
||||
index-alist))
|
||||
(when index-inst-alist
|
||||
(push (cons "Instances"
|
||||
(sort index-inst-alist 'haskell-ds-imenu-label-cmp))
|
||||
index-alist))
|
||||
(when index-imp-alist
|
||||
(push (cons "Imports"
|
||||
(sort index-imp-alist 'haskell-ds-imenu-label-cmp))
|
||||
index-alist))
|
||||
(when index-class-alist
|
||||
(push (cons "Classes"
|
||||
(sort index-class-alist 'haskell-ds-imenu-label-cmp))
|
||||
index-alist))
|
||||
(when index-var-alist
|
||||
(if haskell-decl-scan-bindings-as-variables
|
||||
(push (cons "Variables"
|
||||
(sort index-var-alist 'haskell-ds-imenu-label-cmp))
|
||||
index-alist)
|
||||
(setq index-alist (append index-alist
|
||||
(sort index-var-alist 'haskell-ds-imenu-label-cmp)))))
|
||||
(message "Sorting declarations in %s...done" bufname)
|
||||
;; Return the alist.
|
||||
index-alist))
|
||||
|
||||
(defun haskell-ds-imenu-label-cmp (el1 el2)
|
||||
"Predicate to compare labels in lists from `haskell-ds-create-imenu-index'."
|
||||
(string< (car el1) (car el2)))
|
||||
|
||||
(defun haskell-ds-imenu ()
|
||||
"Install `imenu' for Haskell scripts."
|
||||
(setq imenu-create-index-function 'haskell-ds-create-imenu-index)
|
||||
(when haskell-decl-scan-add-to-menubar
|
||||
(imenu-add-to-menubar "Declarations")))
|
||||
|
||||
;; The main functions to turn on declaration scanning.
|
||||
;;;###autoload
|
||||
(defun turn-on-haskell-decl-scan ()
|
||||
"Unconditionally activate `haskell-decl-scan-mode'."
|
||||
(interactive)
|
||||
(haskell-decl-scan-mode))
|
||||
(make-obsolete 'turn-on-haskell-decl-scan
|
||||
'haskell-decl-scan-mode
|
||||
"2015-07-23")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode haskell-decl-scan-mode
|
||||
"Toggle Haskell declaration scanning minor mode on or off.
|
||||
With a prefix argument ARG, enable minor mode if ARG is
|
||||
positive, and disable it otherwise. If called from Lisp, enable
|
||||
the mode if ARG is omitted or nil, and toggle it if ARG is `toggle'.
|
||||
|
||||
See also info node `(haskell-mode)haskell-decl-scan-mode' for
|
||||
more details about this minor mode.
|
||||
|
||||
Top-level declarations are scanned and listed in the menu item
|
||||
\"Declarations\" (if enabled via option
|
||||
`haskell-decl-scan-add-to-menubar'). Selecting an item from this
|
||||
menu will take point to the start of the declaration.
|
||||
|
||||
\\[beginning-of-defun] and \\[end-of-defun] move forward and backward to the start of a declaration.
|
||||
|
||||
This may link with `haskell-doc-mode'.
|
||||
|
||||
For non-literate and LaTeX-style literate scripts, we assume the
|
||||
common convention that top-level declarations start at the first
|
||||
column. For Bird-style literate scripts, we assume the common
|
||||
convention that top-level declarations start at the third column,
|
||||
ie. after \"> \".
|
||||
|
||||
Anything in `font-lock-comment-face' is not considered for a
|
||||
declaration. Therefore, using Haskell font locking with comments
|
||||
coloured in `font-lock-comment-face' improves declaration scanning.
|
||||
|
||||
Literate Haskell scripts are supported: If the value of
|
||||
`haskell-literate' (set automatically by `literate-haskell-mode')
|
||||
is `bird', a Bird-style literate script is assumed. If it is nil
|
||||
or `tex', a non-literate or LaTeX-style literate script is
|
||||
assumed, respectively.
|
||||
|
||||
Invokes `haskell-decl-scan-mode-hook' on activation."
|
||||
:group 'haskell-decl-scan
|
||||
|
||||
(kill-local-variable 'beginning-of-defun-function)
|
||||
(kill-local-variable 'end-of-defun-function)
|
||||
(kill-local-variable 'imenu-create-index-function)
|
||||
(unless haskell-decl-scan-mode
|
||||
;; How can we cleanly remove the "Declarations" menu?
|
||||
(when haskell-decl-scan-add-to-menubar
|
||||
(local-set-key [menu-bar index] nil)))
|
||||
|
||||
(when haskell-decl-scan-mode
|
||||
(setq-local beginning-of-defun-function 'haskell-ds-backward-decl)
|
||||
(setq-local end-of-defun-function 'haskell-ds-forward-decl)
|
||||
(haskell-ds-imenu)))
|
||||
|
||||
|
||||
;; Provide ourselves:
|
||||
|
||||
(provide 'haskell-decl-scan)
|
||||
|
||||
;;; haskell-decl-scan.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-decl-scan.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-decl-scan.elc
Normal file
Binary file not shown.
1861
elpa/haskell-mode-20171022.26/haskell-doc.el
Normal file
1861
elpa/haskell-mode-20171022.26/haskell-doc.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-doc.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-doc.elc
Normal file
Binary file not shown.
711
elpa/haskell-mode-20171022.26/haskell-font-lock.el
Normal file
711
elpa/haskell-mode-20171022.26/haskell-font-lock.el
Normal file
@@ -0,0 +1,711 @@
|
||||
;;; haskell-font-lock.el --- Font locking module for Haskell Mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
;; Copyright 1997-1998 Graeme E Moss, and Tommy Thorn
|
||||
|
||||
;; Author: 1997-1998 Graeme E Moss <gem@cs.york.ac.uk>
|
||||
;; 1997-1998 Tommy Thorn <thorn@irisa.fr>
|
||||
;; 2003 Dave Love <fx@gnu.org>
|
||||
;; Keywords: faces files Haskell
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-compat)
|
||||
(require 'haskell-lexeme)
|
||||
(require 'font-lock)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup haskell-appearance nil
|
||||
"Haskell Appearance."
|
||||
:group 'haskell)
|
||||
|
||||
|
||||
(defcustom haskell-font-lock-symbols nil
|
||||
"Display \\ and -> and such using symbols in fonts.
|
||||
|
||||
This may sound like a neat trick, but be extra careful: it changes the
|
||||
alignment and can thus lead to nasty surprises with regards to layout."
|
||||
:group 'haskell-appearance
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom haskell-font-lock-symbols-alist
|
||||
'(("\\" . "λ")
|
||||
("not" . "¬")
|
||||
("->" . "→")
|
||||
("<-" . "←")
|
||||
("=>" . "⇒")
|
||||
("()" . "∅")
|
||||
("==" . "≡")
|
||||
("/=" . "≢")
|
||||
(">=" . "≥")
|
||||
("<=" . "≤")
|
||||
("!!" . "‼")
|
||||
("&&" . "∧")
|
||||
("||" . "∨")
|
||||
("sqrt" . "√")
|
||||
("undefined" . "⊥")
|
||||
("pi" . "π")
|
||||
("~>" . "⇝") ;; Omega language
|
||||
;; ("~>" "↝") ;; less desirable
|
||||
("-<" . "↢") ;; Paterson's arrow syntax
|
||||
;; ("-<" "⤙") ;; nicer but uncommon
|
||||
("::" . "∷")
|
||||
("." "∘" ; "○"
|
||||
;; Need a predicate here to distinguish the . used by
|
||||
;; forall <foo> . <bar>.
|
||||
haskell-font-lock-dot-is-not-composition)
|
||||
("forall" . "∀"))
|
||||
"Alist mapping Haskell symbols to chars.
|
||||
|
||||
Each element has the form (STRING . COMPONENTS) or (STRING
|
||||
COMPONENTS PREDICATE).
|
||||
|
||||
STRING is the Haskell symbol.
|
||||
COMPONENTS is a representation specification suitable as an argument to
|
||||
`compose-region'.
|
||||
PREDICATE if present is a function of one argument (the start position
|
||||
of the symbol) which should return non-nil if this mapping should
|
||||
be disabled at that position."
|
||||
:type '(alist string string)
|
||||
:group 'haskell-appearance)
|
||||
|
||||
(defcustom haskell-font-lock-keywords
|
||||
;; `as', `hiding', and `qualified' are part of the import
|
||||
;; spec syntax, but they are not reserved.
|
||||
;; `_' can go in here since it has temporary word syntax.
|
||||
'("case" "class" "data" "default" "deriving" "do"
|
||||
"else" "if" "import" "in" "infix" "infixl"
|
||||
"infixr" "instance" "let" "module" "mdo" "newtype" "of"
|
||||
"rec" "pattern" "proc" "then" "type" "where" "_")
|
||||
"Identifiers treated as reserved keywords in Haskell."
|
||||
:group 'haskell-appearance
|
||||
:type '(repeat string))
|
||||
|
||||
|
||||
(defun haskell-font-lock-dot-is-not-composition (start)
|
||||
"Return non-nil if the \".\" at START is not a composition operator.
|
||||
This is the case if the \".\" is part of a \"forall <tvar> . <type>\"."
|
||||
(save-excursion
|
||||
(goto-char start)
|
||||
(or (re-search-backward "\\<forall\\>[^.\"]*\\="
|
||||
(line-beginning-position) t)
|
||||
(not (or
|
||||
(string= " " (string (char-after start)))
|
||||
(null (char-before start))
|
||||
(string= " " (string (char-before start))))))))
|
||||
|
||||
(defvar haskell-yesod-parse-routes-mode-keywords
|
||||
'(("^\\([^ \t\n]+\\)\\(?:[ \t]+\\([^ \t\n]+\\)\\)?"
|
||||
(1 'font-lock-string-face)
|
||||
(2 'haskell-constructor-face nil lax))))
|
||||
|
||||
(define-derived-mode haskell-yesod-parse-routes-mode text-mode "Yesod parseRoutes mode"
|
||||
"Mode for parseRoutes from Yesod."
|
||||
(setq-local font-lock-defaults '(haskell-yesod-parse-routes-mode-keywords t t nil nil)))
|
||||
|
||||
(defcustom haskell-font-lock-quasi-quote-modes
|
||||
`(("hsx" . xml-mode)
|
||||
("hamlet" . shakespeare-hamlet-mode)
|
||||
("shamlet" . shakespeare-hamlet-mode)
|
||||
("whamlet" . shakespeare-hamlet-mode)
|
||||
("xmlQQ" . xml-mode)
|
||||
("xml" . xml-mode)
|
||||
("cmd" . shell-mode)
|
||||
("sh_" . shell-mode)
|
||||
("jmacro" . javascript-mode)
|
||||
("jmacroE" . javascript-mode)
|
||||
("r" . ess-mode)
|
||||
("rChan" . ess-mode)
|
||||
("sql" . sql-mode)
|
||||
("json" . json-mode)
|
||||
("aesonQQ" . json-mode)
|
||||
("parseRoutes" . haskell-yesod-parse-routes-mode))
|
||||
"Mapping from quasi quoter token to fontification mode.
|
||||
|
||||
If a quasi quote is seen in Haskell code its contents will have
|
||||
font faces assigned as if respective mode was enabled."
|
||||
:group 'haskell-appearance
|
||||
:type '(repeat (cons string symbol)))
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-keyword-face
|
||||
'((t :inherit font-lock-keyword-face))
|
||||
"Face used to highlight Haskell keywords."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-type-face
|
||||
'((t :inherit font-lock-type-face))
|
||||
"Face used to highlight Haskell types"
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-constructor-face
|
||||
'((t :inherit font-lock-type-face))
|
||||
"Face used to highlight Haskell constructors."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;; This used to be `font-lock-variable-name-face' but it doesn't result in
|
||||
;; a highlighting that's consistent with other modes (it's mostly used
|
||||
;; for function defintions).
|
||||
(defface haskell-definition-face
|
||||
'((t :inherit font-lock-function-name-face))
|
||||
"Face used to highlight Haskell definitions."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;; This is probably just wrong, but it used to use
|
||||
;; `font-lock-function-name-face' with a result that was not consistent with
|
||||
;; other major modes, so I just exchanged with `haskell-definition-face'.
|
||||
;;;###autoload
|
||||
(defface haskell-operator-face
|
||||
'((t :inherit font-lock-variable-name-face))
|
||||
"Face used to highlight Haskell operators."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-pragma-face
|
||||
'((t :inherit font-lock-preprocessor-face))
|
||||
"Face used to highlight Haskell pragmas ({-# ... #-})."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-liquid-haskell-annotation-face
|
||||
'((t :inherit haskell-pragma-face))
|
||||
"Face used to highlight LiquidHaskell annotations ({-@ ... @-})."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-literate-comment-face
|
||||
'((t :inherit font-lock-doc-face))
|
||||
"Face with which to fontify literate comments.
|
||||
Inherit from `default' to avoid fontification of them."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
(defface haskell-quasi-quote-face
|
||||
'((t :inherit font-lock-string-face))
|
||||
"Generic face for quasiquotes.
|
||||
|
||||
Some quote types are fontified according to other mode defined in
|
||||
`haskell-font-lock-quasi-quote-modes'."
|
||||
:group 'haskell-appearance)
|
||||
|
||||
(defun haskell-font-lock-compose-symbol (alist)
|
||||
"Compose a sequence of ascii chars into a symbol.
|
||||
Regexp match data 0 points to the chars."
|
||||
;; Check that the chars should really be composed into a symbol.
|
||||
(let* ((start (match-beginning 0))
|
||||
(end (match-end 0))
|
||||
(syntaxes (cond
|
||||
((eq (char-syntax (char-after start)) ?w) '(?w))
|
||||
((eq (char-syntax (char-after start)) ?.) '(?.))
|
||||
;; Special case for the . used for qualified names.
|
||||
((and (eq (char-after start) ?\.) (= end (1+ start)))
|
||||
'(?_ ?\\ ?w))
|
||||
(t '(?_ ?\\))))
|
||||
sym-data)
|
||||
(if (or (memq (char-syntax (or (char-before start) ?\ )) syntaxes)
|
||||
(memq (char-syntax (or (char-after end) ?\ )) syntaxes)
|
||||
(or (elt (syntax-ppss) 3) (elt (syntax-ppss) 4))
|
||||
(and (consp (setq sym-data (cdr (assoc (match-string 0) alist))))
|
||||
(let ((pred (cadr sym-data)))
|
||||
(setq sym-data (car sym-data))
|
||||
(funcall pred start))))
|
||||
;; No composition for you. Let's actually remove any composition
|
||||
;; we may have added earlier and which is now incorrect.
|
||||
(remove-text-properties start end '(composition))
|
||||
;; That's a symbol alright, so add the composition.
|
||||
(compose-region start end sym-data)))
|
||||
;; Return nil because we're not adding any face property.
|
||||
nil)
|
||||
|
||||
(defun haskell-font-lock-symbols-keywords ()
|
||||
(when (and haskell-font-lock-symbols
|
||||
haskell-font-lock-symbols-alist)
|
||||
`((,(regexp-opt (mapcar 'car haskell-font-lock-symbols-alist) t)
|
||||
(0 (haskell-font-lock-compose-symbol ',haskell-font-lock-symbols-alist)
|
||||
;; In Emacs-21, if the `override' field is nil, the face
|
||||
;; expressions is only evaluated if the text has currently
|
||||
;; no face. So force evaluation by using `keep'.
|
||||
keep)))))
|
||||
|
||||
(defun haskell-font-lock--forward-type (&optional ignore)
|
||||
"Find where does this type declaration end.
|
||||
|
||||
Moves the point to the end of type declaration. It should be
|
||||
invoked with point just after one of type introducing keywords
|
||||
like ::, class, instance, data, newtype, type."
|
||||
(interactive)
|
||||
(let ((cont t)
|
||||
(end (point))
|
||||
(token nil)
|
||||
;; we are starting right after ::
|
||||
(last-token-was-operator t)
|
||||
(last-token-was-newline nil)
|
||||
(open-parens 0))
|
||||
(while cont
|
||||
(setq token (haskell-lexeme-looking-at-token 'newline))
|
||||
|
||||
(cond
|
||||
((null token)
|
||||
(setq cont nil))
|
||||
((member token '(newline))
|
||||
(setq last-token-was-newline (not last-token-was-operator))
|
||||
(setq end (match-end 0))
|
||||
(goto-char (match-end 0)))
|
||||
((member (match-string-no-properties 0)
|
||||
'(")" "]" "}"))
|
||||
(setq open-parens (1- open-parens))
|
||||
(if (< open-parens 0)
|
||||
;; unmatched closing parenthesis closes type declaration
|
||||
(setq cont nil)
|
||||
(setq end (match-end 0))
|
||||
(goto-char end))
|
||||
(setq last-token-was-newline nil))
|
||||
((and (member (match-string-no-properties 0)
|
||||
'("," ";" "|"))
|
||||
(not (member (match-string-no-properties 0) ignore)))
|
||||
(if (equal 0 open-parens)
|
||||
(setq cont nil)
|
||||
(setq last-token-was-operator t)
|
||||
(setq end (match-end 0))
|
||||
(goto-char end))
|
||||
(setq last-token-was-newline nil))
|
||||
((and (or (member (match-string-no-properties 0)
|
||||
'("<-" "=" "←"))
|
||||
(member (match-string-no-properties 0) haskell-font-lock-keywords))
|
||||
(not (member (match-string-no-properties 0) ignore)))
|
||||
(setq cont nil)
|
||||
(setq last-token-was-newline nil))
|
||||
((member (match-string-no-properties 0)
|
||||
'("(" "[" "{"))
|
||||
(if last-token-was-newline
|
||||
(setq cont nil)
|
||||
(setq open-parens (1+ open-parens))
|
||||
(setq end (match-end 0))
|
||||
(goto-char end)
|
||||
(setq last-token-was-newline nil)))
|
||||
((member token '(qsymid char string number template-haskell-quote template-haskell-quasi-quote))
|
||||
(setq last-token-was-operator (member (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1)))
|
||||
'(varsym consym)))
|
||||
(if (and (not last-token-was-operator) last-token-was-newline)
|
||||
(setq cont nil)
|
||||
|
||||
(goto-char (match-end 0))
|
||||
(setq end (point)))
|
||||
(setq last-token-was-newline nil))
|
||||
((member token '(comment nested-comment literate-comment))
|
||||
(goto-char (match-end 0))
|
||||
(setq end (point)))
|
||||
(t
|
||||
(goto-char (match-end 0))
|
||||
(setq end (point))
|
||||
(setq last-token-was-newline nil))))
|
||||
(goto-char end)))
|
||||
|
||||
|
||||
(defun haskell-font-lock--select-face-on-type-or-constructor ()
|
||||
"Private function used to select either type or constructor face
|
||||
on an uppercase identifier."
|
||||
(cl-case (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1)))
|
||||
(varid (let ((word (match-string-no-properties 0)))
|
||||
(cond
|
||||
((member word haskell-font-lock-keywords)
|
||||
;; Note: keywords parse as keywords only when not qualified.
|
||||
;; GHC parses Control.let as a single but illegal lexeme.
|
||||
(when (member word '("class" "instance" "type" "data" "newtype"))
|
||||
(save-excursion
|
||||
(goto-char (match-end 0))
|
||||
(save-match-data
|
||||
(haskell-font-lock--forward-type
|
||||
(cond
|
||||
((member word '("class" "instance"))
|
||||
'("|"))
|
||||
((member word '("type"))
|
||||
;; Need to support 'type instance'
|
||||
'("=" "instance")))))
|
||||
(add-text-properties (match-end 0) (point) '(font-lock-multiline t haskell-type t))))
|
||||
'haskell-keyword-face)
|
||||
((member word '("forall"))
|
||||
(when (get-text-property (match-beginning 0) 'haskell-type)
|
||||
'haskell-keyword-face)))))
|
||||
(conid (if (get-text-property (match-beginning 0) 'haskell-type)
|
||||
'haskell-type-face
|
||||
'haskell-constructor-face))
|
||||
(varsym (unless (and (member (match-string 0) '("-" "+" "."))
|
||||
(equal (string-to-syntax "w") (syntax-after (match-beginning 0))))
|
||||
;; We need to protect against the case of
|
||||
;; plus, minus or dot inside a floating
|
||||
;; point number.
|
||||
'haskell-operator-face))
|
||||
(consym (if (not (member (match-string 1) '("::" "∷")))
|
||||
(if (get-text-property (match-beginning 0) 'haskell-type)
|
||||
'haskell-type-face
|
||||
'haskell-constructor-face)
|
||||
(save-excursion
|
||||
(goto-char (match-end 0))
|
||||
(save-match-data
|
||||
(haskell-font-lock--forward-type))
|
||||
(add-text-properties (match-end 0) (point) '(font-lock-multiline t haskell-type t)))
|
||||
'haskell-operator-face))))
|
||||
|
||||
(defun haskell-font-lock--put-face-on-type-or-constructor ()
|
||||
"Private function used to put either type or constructor face
|
||||
on an uppercase identifier."
|
||||
(let ((face (haskell-font-lock--select-face-on-type-or-constructor)))
|
||||
(when (and face
|
||||
(not (text-property-not-all (match-beginning 0) (match-end 0) 'face nil)))
|
||||
(put-text-property (match-beginning 0) (match-end 0) 'face face))))
|
||||
|
||||
|
||||
(defun haskell-font-lock-keywords ()
|
||||
;; this has to be a function because it depends on global value of
|
||||
;; `haskell-font-lock-symbols'
|
||||
"Generate font lock eywords."
|
||||
(let* (;; Bird-style literate scripts start a line of code with
|
||||
;; "^>", otherwise a line of code starts with "^".
|
||||
(line-prefix "^\\(?:> ?\\)?")
|
||||
|
||||
(varid "[[:lower:]_][[:alnum:]'_]*")
|
||||
;; We allow ' preceding conids because of DataKinds/PolyKinds
|
||||
(conid "'?[[:upper:]][[:alnum:]'_]*")
|
||||
(sym "\\s.+")
|
||||
|
||||
;; Top-level declarations
|
||||
(topdecl-var
|
||||
(concat line-prefix "\\(" varid "\\(?:\\s-*,\\s-*" varid "\\)*" "\\)"
|
||||
;; optionally allow for a single newline after identifier
|
||||
"\\(\\s-+\\|\\s-*[\n]\\s-+\\)"
|
||||
;; A toplevel declaration can be followed by a definition
|
||||
;; (=), a type (::) or (∷), a guard, or a pattern which can
|
||||
;; either be a variable, a constructor, a parenthesized
|
||||
;; thingy, or an integer or a string.
|
||||
"\\(" varid "\\|" conid "\\|::\\|∷\\|=\\||\\|\\s(\\|[0-9\"']\\)"))
|
||||
(topdecl-var2
|
||||
(concat line-prefix "\\(" varid "\\|" conid "\\)\\s-*`\\(" varid "\\)`"))
|
||||
(topdecl-bangpat
|
||||
(concat line-prefix "\\(" varid "\\)\\s-*!"))
|
||||
(topdecl-sym
|
||||
(concat line-prefix "\\(" varid "\\|" conid "\\)\\s-*\\(" sym "\\)"))
|
||||
(topdecl-sym2 (concat line-prefix "(\\(" sym "\\))"))
|
||||
|
||||
keywords)
|
||||
|
||||
(setq keywords
|
||||
`(;; NOTICE the ordering below is significant
|
||||
;;
|
||||
("^#\\(?:[^\\\n]\\|\\\\\\(?:.\\|\n\\|\\'\\)\\)*\\(?:\n\\|\\'\\)" 0 'font-lock-preprocessor-face t)
|
||||
|
||||
,@(haskell-font-lock-symbols-keywords)
|
||||
|
||||
;; Special case for `as', `hiding', `safe' and `qualified', which are
|
||||
;; keywords in import statements but are not otherwise reserved.
|
||||
("\\<import[ \t]+\\(?:\\(safe\\>\\)[ \t]*\\)?\\(?:\\(qualified\\>\\)[ \t]*\\)?\\(?:\"[^\"]*\"[\t ]*\\)?[^ \t\n()]+[ \t]*\\(?:\\(\\<as\\>\\)[ \t]*[^ \t\n()]+[ \t]*\\)?\\(\\<hiding\\>\\)?"
|
||||
(1 'haskell-keyword-face nil lax)
|
||||
(2 'haskell-keyword-face nil lax)
|
||||
(3 'haskell-keyword-face nil lax)
|
||||
(4 'haskell-keyword-face nil lax))
|
||||
|
||||
;; Special case for `foreign import'
|
||||
;; keywords in foreign import statements but are not otherwise reserved.
|
||||
("\\<\\(foreign\\)[ \t]+\\(import\\)[ \t]+\\(?:\\(ccall\\|stdcall\\|cplusplus\\|jvm\\|dotnet\\)[ \t]+\\)?\\(?:\\(safe\\|unsafe\\|interruptible\\)[ \t]+\\)?"
|
||||
(1 'haskell-keyword-face nil lax)
|
||||
(2 'haskell-keyword-face nil lax)
|
||||
(3 'haskell-keyword-face nil lax)
|
||||
(4 'haskell-keyword-face nil lax))
|
||||
|
||||
;; Special case for `foreign export'
|
||||
;; keywords in foreign export statements but are not otherwise reserved.
|
||||
("\\<\\(foreign\\)[ \t]+\\(export\\)[ \t]+\\(?:\\(ccall\\|stdcall\\|cplusplus\\|jvm\\|dotnet\\)[ \t]+\\)?"
|
||||
(1 'haskell-keyword-face nil lax)
|
||||
(2 'haskell-keyword-face nil lax)
|
||||
(3 'haskell-keyword-face nil lax))
|
||||
|
||||
;; Special case for `type family' and `data family'.
|
||||
;; `family' is only reserved in these contexts.
|
||||
("\\<\\(type\\|data\\)[ \t]+\\(family\\>\\)"
|
||||
(1 'haskell-keyword-face nil lax)
|
||||
(2 'haskell-keyword-face nil lax))
|
||||
|
||||
;; Special case for `type role'
|
||||
;; `role' is only reserved in this context.
|
||||
("\\<\\(type\\)[ \t]+\\(role\\>\\)"
|
||||
(1 'haskell-keyword-face nil lax)
|
||||
(2 'haskell-keyword-face nil lax))
|
||||
|
||||
;; Toplevel Declarations.
|
||||
;; Place them *before* generic id-and-op highlighting.
|
||||
(,topdecl-var (1 (unless (member (match-string 1) haskell-font-lock-keywords)
|
||||
'haskell-definition-face)))
|
||||
(,topdecl-var2 (2 (unless (member (match-string 2) haskell-font-lock-keywords)
|
||||
'haskell-definition-face)))
|
||||
(,topdecl-bangpat (1 (unless (member (match-string 1) haskell-font-lock-keywords)
|
||||
'haskell-definition-face)))
|
||||
(,topdecl-sym (2 (unless (member (match-string 2) '("\\" "=" "->" "→" "<-" "←" "::" "∷" "," ";" "`"))
|
||||
'haskell-definition-face)))
|
||||
(,topdecl-sym2 (1 (unless (member (match-string 1) '("\\" "=" "->" "→" "<-" "←" "::" "∷" "," ";" "`"))
|
||||
'haskell-definition-face)))
|
||||
|
||||
;; These four are debatable...
|
||||
("(\\(,*\\|->\\))" 0 'haskell-constructor-face)
|
||||
("\\[\\]" 0 'haskell-constructor-face)
|
||||
|
||||
("`"
|
||||
(0 (if (or (elt (syntax-ppss) 3) (elt (syntax-ppss) 4))
|
||||
(parse-partial-sexp (point) (point-max) nil nil (syntax-ppss)
|
||||
'syntax-table)
|
||||
(when (save-excursion
|
||||
(goto-char (match-beginning 0))
|
||||
(haskell-lexeme-looking-at-backtick))
|
||||
(goto-char (match-end 0))
|
||||
(unless (text-property-not-all (match-beginning 1) (match-end 1) 'face nil)
|
||||
(put-text-property (match-beginning 1) (match-end 1) 'face 'haskell-operator-face))
|
||||
(unless (text-property-not-all (match-beginning 2) (match-end 2) 'face nil)
|
||||
(put-text-property (match-beginning 2) (match-end 2) 'face 'haskell-operator-face))
|
||||
(unless (text-property-not-all (match-beginning 4) (match-end 4) 'face nil)
|
||||
(put-text-property (match-beginning 4) (match-end 4) 'face 'haskell-operator-face))
|
||||
(add-text-properties
|
||||
(match-beginning 0) (match-end 0)
|
||||
'(font-lock-fontified t fontified t font-lock-multiline t))))))
|
||||
|
||||
(,haskell-lexeme-idsym-first-char
|
||||
(0 (if (or (elt (syntax-ppss) 3) (elt (syntax-ppss) 4))
|
||||
(parse-partial-sexp (point) (point-max) nil nil (syntax-ppss)
|
||||
'syntax-table)
|
||||
(when (save-excursion
|
||||
(goto-char (match-beginning 0))
|
||||
(haskell-lexeme-looking-at-qidsym))
|
||||
(goto-char (match-end 0))
|
||||
;; note that we have to put face ourselves here because font-lock
|
||||
;; will use match data from the original matcher
|
||||
(haskell-font-lock--put-face-on-type-or-constructor)))))))
|
||||
keywords))
|
||||
|
||||
|
||||
(defun haskell-font-lock-fontify-block (lang-mode start end)
|
||||
"Fontify a block as LANG-MODE."
|
||||
(let ((string (buffer-substring-no-properties start end))
|
||||
(modified (buffer-modified-p))
|
||||
(org-buffer (current-buffer)) pos next)
|
||||
(remove-text-properties start end '(face nil))
|
||||
(with-current-buffer
|
||||
(get-buffer-create
|
||||
(concat " haskell-font-lock-fontify-block:" (symbol-name lang-mode)))
|
||||
(delete-region (point-min) (point-max))
|
||||
(insert string " ") ;; so there's a final property change
|
||||
(cl-letf (((symbol-function 'message)
|
||||
(lambda (_fmt &rest _args))))
|
||||
;; silence messages
|
||||
(unless (eq major-mode lang-mode) (funcall lang-mode))
|
||||
(font-lock-ensure))
|
||||
(setq pos (point-min))
|
||||
(while (setq next (next-single-property-change pos 'face))
|
||||
(put-text-property
|
||||
(+ start (1- pos)) (1- (+ start next)) 'face
|
||||
(or (get-text-property pos 'face) 'default) org-buffer)
|
||||
(setq pos next))
|
||||
(unless (equal pos (point-max))
|
||||
(put-text-property
|
||||
(+ start (1- pos)) (1- (+ start (point-max))) 'face
|
||||
'default org-buffer)))
|
||||
(add-text-properties
|
||||
start end
|
||||
'(font-lock-fontified t fontified t font-lock-multiline t))
|
||||
(set-buffer-modified-p modified)))
|
||||
|
||||
(defun haskell-syntactic-face-function (state)
|
||||
"`font-lock-syntactic-face-function' for Haskell."
|
||||
(cond
|
||||
((nth 3 state)
|
||||
(if (equal ?| (nth 3 state))
|
||||
;; find out what kind of QuasiQuote is this
|
||||
(let* ((qqname (save-excursion
|
||||
(goto-char (nth 8 state))
|
||||
(skip-syntax-backward "w._")
|
||||
(buffer-substring-no-properties (point) (nth 8 state))))
|
||||
(lang-mode (cdr (assoc qqname haskell-font-lock-quasi-quote-modes))))
|
||||
|
||||
(if (and lang-mode
|
||||
(fboundp lang-mode))
|
||||
(save-excursion
|
||||
;; find the end of the QuasiQuote
|
||||
(parse-partial-sexp (point) (point-max) nil nil state
|
||||
'syntax-table)
|
||||
(haskell-font-lock-fontify-block lang-mode (1+ (nth 8 state)) (1- (point)))
|
||||
;; must return nil here so that it is not fontified again as string
|
||||
nil)
|
||||
;; fontify normally as string because lang-mode is not present
|
||||
'haskell-quasi-quote-face))
|
||||
(save-excursion
|
||||
(let
|
||||
((state2
|
||||
(parse-partial-sexp (point) (point-max) nil nil state
|
||||
'syntax-table))
|
||||
(end-of-string (point)))
|
||||
|
||||
(put-text-property (nth 8 state) (point)
|
||||
'face 'font-lock-string-face)
|
||||
|
||||
|
||||
(if (or (equal t (nth 3 state)) (nth 3 state2))
|
||||
;; This is an unterminated string constant, use warning
|
||||
;; face for the opening quote.
|
||||
(put-text-property (nth 8 state) (1+ (nth 8 state))
|
||||
'face 'font-lock-warning-face))
|
||||
|
||||
(goto-char (1+ (nth 8 state)))
|
||||
(while (re-search-forward "\\\\" end-of-string t)
|
||||
|
||||
(goto-char (1- (point)))
|
||||
|
||||
(if (looking-at haskell-lexeme-string-literal-inside-item)
|
||||
(goto-char (match-end 0))
|
||||
|
||||
;; We are looking at an unacceptable escape
|
||||
;; sequence. Use warning face to highlight that.
|
||||
(put-text-property (point) (1+ (point))
|
||||
'face 'font-lock-warning-face)
|
||||
(goto-char (1+ (point)))))))
|
||||
;; must return nil here so that it is not fontified again as string
|
||||
nil))
|
||||
;; Detect literate comment lines starting with syntax class '<'
|
||||
((save-excursion
|
||||
(goto-char (nth 8 state))
|
||||
(equal (string-to-syntax "<") (syntax-after (point))))
|
||||
'haskell-literate-comment-face)
|
||||
;; Detect pragmas. A pragma is enclosed in special comment
|
||||
;; delimiters {-# .. #-}.
|
||||
((save-excursion
|
||||
(goto-char (nth 8 state))
|
||||
(and (looking-at-p "{-#")
|
||||
(forward-comment 1)
|
||||
(goto-char (- (point) 3))
|
||||
(looking-at-p "#-}")))
|
||||
'haskell-pragma-face)
|
||||
;; Detect Liquid Haskell annotations enclosed in special comment
|
||||
;; delimiters {-@ .. @-}.
|
||||
((save-excursion
|
||||
(goto-char (nth 8 state))
|
||||
(and (looking-at-p "{-@")
|
||||
(forward-comment 1)
|
||||
(goto-char (- (point) 3))
|
||||
(looking-at-p "@-}")))
|
||||
'haskell-liquid-haskell-annotation-face)
|
||||
;; Haddock comment start with either "-- [|^*$]" or "{- ?[|^*$]"
|
||||
;; (note space optional for nested comments and mandatory for
|
||||
;; double dash comments).
|
||||
;;
|
||||
;; Haddock comment will also continue on next line, provided:
|
||||
;; - current line is a double dash haddock comment
|
||||
;; - next line is also double dash comment
|
||||
;; - there is only whitespace between
|
||||
;;
|
||||
;; We recognize double dash haddock comments by property
|
||||
;; 'font-lock-doc-face attached to newline. In case of {- -}
|
||||
;; comments newline is outside of comment.
|
||||
((save-excursion
|
||||
(goto-char (nth 8 state))
|
||||
(or (looking-at-p "\\(?:{- ?\\|-- \\)[|^*$]")
|
||||
(and (looking-at-p "--") ; are we at double dash comment
|
||||
(forward-line -1) ; this is nil on first line
|
||||
(eq (get-text-property (line-end-position) 'face)
|
||||
'font-lock-doc-face) ; is a doc face
|
||||
(forward-line)
|
||||
(skip-syntax-forward "-") ; see if there is only whitespace
|
||||
(eq (point) (nth 8 state))))) ; we are back in position
|
||||
;; Here we look inside the comment to see if there are substrings
|
||||
;; worth marking inside we try to emulate as much of haddock as
|
||||
;; possible. First we add comment face all over the comment, then
|
||||
;; we add special features.
|
||||
(let ((beg (nth 8 state))
|
||||
(end (save-excursion
|
||||
(parse-partial-sexp (point) (point-max) nil nil state
|
||||
'syntax-table)
|
||||
(point)))
|
||||
(emphasis-open-point nil)
|
||||
(strong-open-point nil))
|
||||
(put-text-property beg end 'face 'font-lock-doc-face)
|
||||
|
||||
(when (fboundp 'add-face-text-property)
|
||||
;; `add-face-text-property' is not defined in Emacs 23
|
||||
|
||||
;; iterate over chars, take escaped chars unconditionally
|
||||
;; mark when a construct is opened, close and face it when
|
||||
;; it is closed
|
||||
|
||||
(save-excursion
|
||||
(while (< (point) end)
|
||||
(if (looking-at "__\\|\\\\.\\|\\\n\\|[/]")
|
||||
(progn
|
||||
(cond
|
||||
((equal (match-string 0) "/")
|
||||
(if emphasis-open-point
|
||||
(progn
|
||||
(add-face-text-property emphasis-open-point (match-end 0)
|
||||
'(:slant italic))
|
||||
(setq emphasis-open-point nil))
|
||||
(setq emphasis-open-point (point))))
|
||||
((equal (match-string 0) "__")
|
||||
(if strong-open-point
|
||||
(progn
|
||||
(add-face-text-property strong-open-point (match-end 0)
|
||||
'(:weight bold))
|
||||
(setq strong-open-point nil))
|
||||
(setq strong-open-point (point))))
|
||||
(t
|
||||
;; this is a backslash escape sequence, skip over it
|
||||
))
|
||||
(goto-char (match-end 0)))
|
||||
;; skip chars that are not interesting
|
||||
(goto-char (1+ (point)))
|
||||
(skip-chars-forward "^_\\\\/" end))))))
|
||||
nil)
|
||||
(t 'font-lock-comment-face)))
|
||||
|
||||
(defun haskell-font-lock-defaults-create ()
|
||||
"Locally set `font-lock-defaults' for Haskell."
|
||||
(setq-local font-lock-defaults
|
||||
'((haskell-font-lock-keywords)
|
||||
nil nil nil nil
|
||||
(font-lock-syntactic-face-function
|
||||
. haskell-syntactic-face-function)
|
||||
;; Get help from font-lock-syntactic-keywords.
|
||||
(parse-sexp-lookup-properties . t)
|
||||
(font-lock-extra-managed-props . (composition)))))
|
||||
|
||||
(defun haskell-fontify-as-mode (text mode)
|
||||
"Fontify TEXT as MODE, returning the fontified text."
|
||||
(with-temp-buffer
|
||||
(funcall mode)
|
||||
(insert text)
|
||||
(if (fboundp 'font-lock-ensure)
|
||||
(font-lock-ensure)
|
||||
(with-no-warnings (font-lock-fontify-buffer)))
|
||||
(buffer-substring (point-min) (point-max))))
|
||||
|
||||
;; Provide ourselves:
|
||||
|
||||
(provide 'haskell-font-lock)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8-unix
|
||||
;; tab-width: 8
|
||||
;; End:
|
||||
|
||||
;;; haskell-font-lock.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-font-lock.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-font-lock.elc
Normal file
Binary file not shown.
1344
elpa/haskell-mode-20171022.26/haskell-ghc-support.el
Normal file
1344
elpa/haskell-mode-20171022.26/haskell-ghc-support.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-ghc-support.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-ghc-support.elc
Normal file
Binary file not shown.
151
elpa/haskell-mode-20171022.26/haskell-hoogle.el
Normal file
151
elpa/haskell-mode-20171022.26/haskell-hoogle.el
Normal file
@@ -0,0 +1,151 @@
|
||||
;;; haskell-hoogle.el --- Look up Haskell documentation via hoogle or hayoo -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright © 2015 Steve Purcell
|
||||
;; 2016 Arthur Fayzrakhmanov
|
||||
|
||||
;; Author: Steve Purcell <steve@sanityinc.com>
|
||||
;; Keywords: docs
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Functions for looking up documentation with hayoo or hoogle, via
|
||||
;; either local or remote servers.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ansi-color)
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-utils)
|
||||
|
||||
|
||||
(defcustom haskell-hoogle-command
|
||||
(if (executable-find "hoogle") "hoogle")
|
||||
"Name of the command to use to query Hoogle.
|
||||
If nil, use the Hoogle web-site."
|
||||
:group 'haskell
|
||||
:type '(choice (const :tag "Use Web-site" nil)
|
||||
string))
|
||||
|
||||
(defcustom haskell-hoogle-url "http://haskell.org/hoogle/?q=%s"
|
||||
"Default value for hoogle web site."
|
||||
:group 'haskell
|
||||
:type '(choice
|
||||
(const :tag "haskell-org" "http://haskell.org/hoogle/?q=%s")
|
||||
(const :tag "fp-complete" "https://www.stackage.org/lts/hoogle?q=%s")
|
||||
(const :tag "hayoo" "http://hayoo.fh-wedel.de/?query=%s")
|
||||
string))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-hoogle (query &optional info)
|
||||
"Do a Hoogle search for QUERY.
|
||||
When `haskell-hoogle-command' is non-nil, this command runs
|
||||
that. Otherwise, it opens a hoogle search result in the browser.
|
||||
|
||||
If prefix argument INFO is given, then `haskell-hoogle-command'
|
||||
is asked to show extra info for the items matching QUERY.."
|
||||
(interactive
|
||||
(let ((def (haskell-ident-at-point)))
|
||||
(if (and def (symbolp def)) (setq def (symbol-name def)))
|
||||
(list (read-string (if def
|
||||
(format "Hoogle query (default %s): " def)
|
||||
"Hoogle query: ")
|
||||
nil nil def)
|
||||
current-prefix-arg)))
|
||||
(if (null haskell-hoogle-command)
|
||||
(browse-url (format haskell-hoogle-url (url-hexify-string query)))
|
||||
(let ((command (concat haskell-hoogle-command
|
||||
(if info " -i " "")
|
||||
" --color " (shell-quote-argument query))))
|
||||
(with-help-window "*hoogle*"
|
||||
(with-current-buffer standard-output
|
||||
(insert (shell-command-to-string command))
|
||||
(ansi-color-apply-on-region (point-min) (point-max)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'hoogle 'haskell-hoogle)
|
||||
|
||||
(defvar haskell-hoogle-server-process-name "emacs-local-hoogle")
|
||||
(defvar haskell-hoogle-server-buffer-name (format "*%s*" haskell-hoogle-server-process-name))
|
||||
(defvar haskell-hoogle-port-number 49513 "Port number.")
|
||||
(defvar haskell-hoogle-server-process nil "The process handle of the local hoogle server.")
|
||||
|
||||
(defun haskell-hoogle-start-server ()
|
||||
"Start hoogle local server."
|
||||
(interactive)
|
||||
(if (executable-find "hoogle")
|
||||
(unless (haskell-hoogle-server-live-p)
|
||||
(set 'haskell-hoogle-server-process
|
||||
(start-process
|
||||
haskell-hoogle-server-process-name
|
||||
(get-buffer-create haskell-hoogle-server-buffer-name)
|
||||
"hoogle" "server" "-p" (number-to-string haskell-hoogle-port-number))))
|
||||
(error "\"hoogle\" executable not found")))
|
||||
|
||||
(defun haskell-hoogle-server-live-p ()
|
||||
"Whether the hoogle server process is live."
|
||||
(condition-case _err
|
||||
(process-live-p haskell-hoogle-server-process)
|
||||
(error nil)))
|
||||
|
||||
(defun haskell-hoogle-kill-server ()
|
||||
"Kill the hoogle server if it is live."
|
||||
(interactive)
|
||||
(when (haskell-hoogle-server-live-p)
|
||||
(kill-process (get-buffer-create haskell-hoogle-server-buffer-name))
|
||||
(set 'haskell-hoogle-server-process nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-hoogle-lookup-from-local ()
|
||||
"Lookup by local hoogle."
|
||||
(interactive)
|
||||
(if (haskell-hoogle-server-live-p)
|
||||
(browse-url (format "http://localhost:%i/?hoogle=%s"
|
||||
haskell-hoogle-port-number
|
||||
(read-string "hoogle: " (haskell-ident-at-point))))
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(when (y-or-n-p "Hoogle server not running, start hoogle server? ")
|
||||
(haskell-hoogle-start-server))
|
||||
(haskell-mode-toggle-interactive-prompt-state t))))
|
||||
|
||||
|
||||
(defcustom haskell-hayoo-url "http://hayoo.fh-wedel.de/?query=%s"
|
||||
"Default value for hayoo web site."
|
||||
:group 'haskell
|
||||
:type '(choice
|
||||
(const :tag "fh-wedel.de" "http://hayoo.fh-wedel.de/?query=%s")
|
||||
string))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-hayoo (query)
|
||||
"Do a Hayoo search for QUERY."
|
||||
(interactive
|
||||
(let ((def (haskell-ident-at-point)))
|
||||
(if (and def (symbolp def)) (setq def (symbol-name def)))
|
||||
(list (read-string (if def
|
||||
(format "Hayoo query (default %s): " def)
|
||||
"Hayoo query: ")
|
||||
nil nil def))))
|
||||
(browse-url (format haskell-hayoo-url (url-hexify-string query))))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'hayoo 'haskell-hayoo)
|
||||
|
||||
|
||||
|
||||
|
||||
(provide 'haskell-hoogle)
|
||||
;;; haskell-hoogle.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-hoogle.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-hoogle.elc
Normal file
Binary file not shown.
1596
elpa/haskell-mode-20171022.26/haskell-indent.el
Normal file
1596
elpa/haskell-mode-20171022.26/haskell-indent.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-indent.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-indent.elc
Normal file
Binary file not shown.
1254
elpa/haskell-mode-20171022.26/haskell-indentation.el
Normal file
1254
elpa/haskell-mode-20171022.26/haskell-indentation.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-indentation.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-indentation.elc
Normal file
Binary file not shown.
1129
elpa/haskell-mode-20171022.26/haskell-interactive-mode.el
Normal file
1129
elpa/haskell-mode-20171022.26/haskell-interactive-mode.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-interactive-mode.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-interactive-mode.elc
Normal file
Binary file not shown.
513
elpa/haskell-mode-20171022.26/haskell-lexeme.el
Normal file
513
elpa/haskell-mode-20171022.26/haskell-lexeme.el
Normal file
@@ -0,0 +1,513 @@
|
||||
;;; haskell-lexeme.el --- haskell lexical tokens -*- coding: utf-8; lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2015 Gracjan Polak
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'rx)
|
||||
|
||||
(unless (category-docstring ?P)
|
||||
(define-category ?P "Haskell symbol constituent characters")
|
||||
(map-char-table
|
||||
#'(lambda (key val)
|
||||
(if (or
|
||||
(and (consp key) (> (car key) 128))
|
||||
(and (numberp key) (> key 128)))
|
||||
(if (member val '(Pc Pd Po Sm Sc Sk So))
|
||||
(modify-category-entry key ?P))))
|
||||
unicode-category-table)
|
||||
|
||||
(dolist (key (string-to-list "!#$%&*+./<=>?@^|~\\-:"))
|
||||
(modify-category-entry key ?P)))
|
||||
|
||||
(defconst haskell-lexeme-modid
|
||||
"[[:upper:]][[:alnum:]'_]*"
|
||||
"Regexp matching a valid Haskell module identifier.
|
||||
|
||||
Note that GHC accepts Unicode category UppercaseLetter as a first
|
||||
character. Following letters are from Unicode categories
|
||||
UppercaseLetter, LowercaseLetter, OtherLetter, TitlecaseLetter,
|
||||
ModifierLetter, DecimalNumber, OtherNumber, backslash or
|
||||
underscore.")
|
||||
|
||||
(defconst haskell-lexeme-id
|
||||
"[[:alpha:]_][[:alnum:]'_]*"
|
||||
"Regexp matching a valid Haskell identifier.
|
||||
|
||||
GHC accepts a string starting with any alphabetic character or
|
||||
underscore followed by any alphanumeric character or underscore
|
||||
or apostrophe.")
|
||||
|
||||
(defconst haskell-lexeme-sym
|
||||
"\\cP+"
|
||||
"Regexp matching a valid Haskell variable or constructor symbol.
|
||||
|
||||
GHC accepts a string of chars from the set
|
||||
[:!#$%&*+./<=>?@^|~\\-] or Unicode category Symbol for chars with
|
||||
codes larger than 128 only.")
|
||||
|
||||
(defconst haskell-lexeme-idsym-first-char
|
||||
"\\(?:[[:alpha:]_]\\|\\cP\\)"
|
||||
"Regexp matching first character of a qualified or unqualified
|
||||
identifier or symbol.
|
||||
|
||||
Useful for `re-search-forward'.")
|
||||
|
||||
(defconst haskell-lexeme-modid-opt-prefix
|
||||
(concat "\\(?:" haskell-lexeme-modid "\\.\\)*")
|
||||
"Regexp matching a valid Haskell module prefix, potentially empty.
|
||||
|
||||
Module path prefix is separated by dots and finishes with a
|
||||
dot. For path component syntax see `haskell-lexeme-modid'.")
|
||||
|
||||
(defconst haskell-lexeme-qid-or-qsym
|
||||
(rx-to-string `(: (regexp ,haskell-lexeme-modid-opt-prefix)
|
||||
(group (| (regexp ,haskell-lexeme-id) (regexp ,haskell-lexeme-sym)
|
||||
))))
|
||||
"Regexp matching a valid qualified identifier or symbol.
|
||||
|
||||
Note that (match-string 1) returns the unqualified part.")
|
||||
|
||||
(defun haskell-lexeme-looking-at-qidsym ()
|
||||
"Non-nil when point is just in front of an optionally qualified
|
||||
identifier or symbol.
|
||||
|
||||
Using this function is more efficient than matching against the
|
||||
regexp `haskell-lexeme-qid-or-qsym'.
|
||||
|
||||
Returns:
|
||||
'qid - if matched a qualified id: 'Data.Map' or 'Map'
|
||||
'qsym - if matched a qualified id: 'Monad.>>=' or '>>='
|
||||
'qprefix - if matched only modid prefix: 'Data.'
|
||||
|
||||
After successful 'qid or 'qsym match (match-string 1) will return
|
||||
the unqualified part (if any)."
|
||||
(let ((begin (point))
|
||||
(match-data-old (match-data)))
|
||||
(save-excursion
|
||||
(while (looking-at (concat haskell-lexeme-modid "\\."))
|
||||
(goto-char (match-end 0)))
|
||||
(cond
|
||||
((looking-at haskell-lexeme-id)
|
||||
(let ((beg (match-beginning 0))
|
||||
(end (match-end 0)))
|
||||
|
||||
;; check is MagicHash is present at the end of the token
|
||||
(goto-char end)
|
||||
(when (looking-at "#+")
|
||||
(setq end (match-end 0)))
|
||||
|
||||
(set-match-data
|
||||
(list begin end
|
||||
beg end)))
|
||||
'qid)
|
||||
((looking-at haskell-lexeme-sym)
|
||||
(set-match-data
|
||||
(list begin (match-end 0)
|
||||
(match-beginning 0) (match-end 0)))
|
||||
'qsym)
|
||||
((equal begin (point))
|
||||
(set-match-data match-data-old)
|
||||
nil)
|
||||
(t
|
||||
(set-match-data
|
||||
(list begin (point)
|
||||
nil nil))
|
||||
'qprefix)))))
|
||||
|
||||
(defun haskell-lexeme-looking-at-backtick ()
|
||||
"Non-nil when point is just in front of an identifier quoted with backticks.
|
||||
|
||||
When match is successful, match-data will contain:
|
||||
(match-text 1) - opening backtick
|
||||
(match-text 2) - whole qualified identifier
|
||||
(match-text 3) - unqualified part of identifier
|
||||
(match-text 4) - closing backtick"
|
||||
(let ((match-data-old (match-data))
|
||||
first-backtick-start
|
||||
last-backtick-start
|
||||
qid-start
|
||||
id-start
|
||||
id-end
|
||||
result)
|
||||
(save-excursion
|
||||
(when (looking-at "`")
|
||||
(setq first-backtick-start (match-beginning 0))
|
||||
(goto-char (match-end 0))
|
||||
(forward-comment (buffer-size))
|
||||
(when (haskell-lexeme-looking-at-qidsym)
|
||||
(setq qid-start (match-beginning 0))
|
||||
(setq id-start (match-beginning 1))
|
||||
(setq id-end (match-end 1))
|
||||
(goto-char (match-end 0))
|
||||
(forward-comment (buffer-size))
|
||||
(when (looking-at "`")
|
||||
(setq last-backtick-start (match-beginning 0))
|
||||
(set-match-data
|
||||
(mapcar
|
||||
(lambda (p)
|
||||
(set-marker (make-marker) p))
|
||||
(list
|
||||
first-backtick-start (1+ last-backtick-start)
|
||||
first-backtick-start (1+ first-backtick-start)
|
||||
qid-start id-end
|
||||
id-start id-end
|
||||
last-backtick-start (1+ last-backtick-start))))
|
||||
(setq result t)))))
|
||||
(unless result
|
||||
(set-match-data match-data-old))
|
||||
result))
|
||||
|
||||
(defconst haskell-lexeme-qid
|
||||
(rx-to-string `(: (regexp "'*")
|
||||
(regexp ,haskell-lexeme-modid-opt-prefix)
|
||||
(group (regexp ,haskell-lexeme-id))))
|
||||
"Regexp matching a valid qualified identifier.
|
||||
|
||||
Note that (match-string 1) returns the unqualified part.")
|
||||
|
||||
(defconst haskell-lexeme-qsym
|
||||
(rx-to-string `(: (regexp "'*")
|
||||
(regexp ,haskell-lexeme-modid-opt-prefix)
|
||||
(group (regexp ,haskell-lexeme-id))))
|
||||
"Regexp matching a valid qualified symbol.
|
||||
|
||||
Note that (match-string 1) returns the unqualified part.")
|
||||
|
||||
(defconst haskell-lexeme-number
|
||||
(rx (| (: (regexp "[0-9]+\\.[0-9]+") (opt (regexp "[eE][-+]?[0-9]+")))
|
||||
(regexp "[0-9]+[eE][-+]?[0-9]+")
|
||||
(regexp "0[xX][0-9a-fA-F]+")
|
||||
(regexp "0[oO][0-7]+")
|
||||
(regexp "[0-9]+")))
|
||||
"Regexp matching a floating point, decimal, octal or hexadecimal number.
|
||||
|
||||
Note that negative sign char is not part of a number.")
|
||||
|
||||
(defconst haskell-lexeme-char-literal-inside
|
||||
(rx (| (not (any "\n'\\"))
|
||||
(: "\\"
|
||||
(| "a" "b" "f" "n" "r" "t" "v" "\\" "\"" "'"
|
||||
"NUL" "SOH" "STX" "ETX" "EOT" "ENQ" "ACK"
|
||||
"BEL" "BS" "HT" "LF" "VT" "FF" "CR" "SO" "SI" "DLE"
|
||||
"DC1" "DC2" "DC3" "DC4" "NAK" "SYN" "ETB" "CAN"
|
||||
"EM" "SUB" "ESC" "FS" "GS" "RS" "US" "SP" "DEL"
|
||||
(regexp "[0-9]+")
|
||||
(: "x" (regexp "[0-9a-fA-F]+"))
|
||||
(: "o" (regexp "[0-7]+"))
|
||||
(: "^" (regexp "[]A-Z@^_\\[]"))))))
|
||||
"Regexp matching an inside of a character literal.
|
||||
|
||||
Note that `haskell-lexeme-char-literal-inside' matches strictly
|
||||
only escape sequences defined in Haskell Report.")
|
||||
|
||||
(defconst haskell-lexeme--char-literal-rx
|
||||
(rx-to-string `(: (group "'")
|
||||
(| (: (group (regexp "[[:alpha:]_([]")) (group "'")) ; exactly one char
|
||||
(: (group (| (regexp "\\\\[^\n][^'\n]*") ; allow quote just after first backslash
|
||||
(regexp "[^[:alpha:]_(['\n][^'\n]*")))
|
||||
(| (group "'") "\n" (regexp "\\'"))))))
|
||||
"Regexp matching a character literal lookalike.
|
||||
|
||||
Note that `haskell-lexeme--char-literal-rx' matches more than
|
||||
Haskell Report specifies because we want to support also code
|
||||
under edit.
|
||||
|
||||
Character literals end with a quote or a newline or end of
|
||||
buffer.
|
||||
|
||||
Regexp has subgroup expressions:
|
||||
(match-text 1) matches the opening quote.
|
||||
(match-text 2) matches the inside of the character literal.
|
||||
(match-text 3) matches the closing quote or an empty string
|
||||
at the end of line or the end buffer.")
|
||||
|
||||
(defun haskell-lexeme-looking-at-char-literal ()
|
||||
"Non-nil when point is at a char literal lookalike.
|
||||
|
||||
Note that this function matches more than Haskell Report
|
||||
specifies because we want to support also code under edit.
|
||||
|
||||
Char literals end with a quote or an unescaped newline or end
|
||||
of buffer.
|
||||
|
||||
After successful match:
|
||||
(match-text 1) matches the opening quote.
|
||||
(match-text 2) matches the inside of the char literla.
|
||||
(match-text 3) matches the closing quote, or a closing
|
||||
newline or is nil when at the end of the buffer."
|
||||
(when (looking-at haskell-lexeme--char-literal-rx)
|
||||
(set-match-data
|
||||
(list (match-beginning 0) (match-end 0)
|
||||
(match-beginning 1) (match-end 1)
|
||||
(or (match-beginning 2) (match-beginning 4)) (or (match-end 2) (match-end 4))
|
||||
(or (match-beginning 3) (match-beginning 5)) (or (match-end 3) (match-end 5))))
|
||||
t))
|
||||
|
||||
(defconst haskell-lexeme-string-literal-inside-item
|
||||
(rx (| (not (any "\n\"\\"))
|
||||
(: "\\"
|
||||
(| "a" "b" "f" "n" "r" "t" "v" "\\" "\"" "'" "&"
|
||||
"NUL" "SOH" "STX" "ETX" "EOT" "ENQ" "ACK"
|
||||
"BEL" "BS" "HT" "LF" "VT" "FF" "CR" "SO" "SI" "DLE"
|
||||
"DC1" "DC2" "DC3" "DC4" "NAK" "SYN" "ETB" "CAN"
|
||||
"EM" "SUB" "ESC" "FS" "GS" "RS" "US" "SP" "DEL"
|
||||
(regexp "[0-9]+")
|
||||
(: "x" (regexp "[0-9a-fA-F]+"))
|
||||
(: "o" (regexp "[0-7]+"))
|
||||
(: "^" (regexp "[]A-Z@^_\\[]"))
|
||||
(regexp "[ \t\n\r\v\f]*\\\\")))))
|
||||
"Regexp matching an item that is a single character or a single
|
||||
escape sequence inside of a string literal.
|
||||
|
||||
Note that `haskell-lexeme-string-literal-inside-item' matches
|
||||
strictly only escape sequences defined in Haskell Report.")
|
||||
|
||||
(defconst haskell-lexeme-string-literal
|
||||
(rx (: (group "\"")
|
||||
(group (* (| (regexp "\\\\[ \t\n\r\v\f]*\\\\")
|
||||
(regexp "\\\\[ \t\n\r\v\f]+")
|
||||
(regexp "\\\\[^ \t\n\r\v\f]")
|
||||
(* (regexp "[^\"\n\\]")))))
|
||||
(group (| "\"" (regexp "$") (regexp "\\\\?\\'")
|
||||
))))
|
||||
"Regexp matching a string literal lookalike.
|
||||
|
||||
Note that `haskell-lexeme-string-literal' matches more than
|
||||
Haskell Report specifies because we want to support also code
|
||||
under edit.
|
||||
|
||||
String literals end with double quote or unescaped newline or end
|
||||
of buffer.
|
||||
|
||||
Regexp has subgroup expressions:
|
||||
(match-text 1) matches the opening double quote.
|
||||
(match-text 2) matches the inside of the string.
|
||||
(match-text 3) matches the closing double quote or an empty string
|
||||
at the end of line or the end buffer.")
|
||||
|
||||
(defun haskell-lexeme-looking-at-string-literal ()
|
||||
"Non-nil when point is at a string literal lookalike.
|
||||
|
||||
Note that this function matches more than Haskell Report
|
||||
specifies because we want to support also code under edit.
|
||||
|
||||
String literals end with double quote or unescaped newline or end
|
||||
of buffer.
|
||||
|
||||
After successful match:
|
||||
(match-text 1) matches the opening doublequote.
|
||||
(match-text 2) matches the inside of the string.
|
||||
(match-text 3) matches the closing quote, or a closing
|
||||
newline or is nil when at the end of the buffer."
|
||||
(when (looking-at "\"")
|
||||
(save-excursion
|
||||
(let ((begin (point)))
|
||||
(goto-char (match-end 0))
|
||||
(let (finish)
|
||||
(while (and (not finish)
|
||||
(re-search-forward "[\"\n\\]" nil 'goto-eob))
|
||||
(cond
|
||||
((equal (match-string 0) "\\")
|
||||
(if (looking-at "[ \t\n\r\v\f]+\\\\?")
|
||||
(goto-char (match-end 0))
|
||||
(goto-char (1+ (point)))))
|
||||
|
||||
((equal (match-string 0) "\"")
|
||||
(set-match-data
|
||||
(list begin (match-end 0)
|
||||
begin (1+ begin)
|
||||
(1+ begin) (match-beginning 0)
|
||||
(match-beginning 0) (match-end 0)))
|
||||
(setq finish t))
|
||||
|
||||
((equal (match-string 0) "\n")
|
||||
(set-match-data
|
||||
(list begin (match-beginning 0)
|
||||
begin (1+ begin)
|
||||
(1+ begin) (match-beginning 0)
|
||||
nil nil))
|
||||
(setq finish t))))
|
||||
(unless finish
|
||||
;; string closed by end of buffer
|
||||
(set-match-data
|
||||
(list begin (point)
|
||||
begin (1+ begin)
|
||||
(1+ begin) (point)
|
||||
nil nil))))))
|
||||
;; there was a match
|
||||
t))
|
||||
|
||||
(defun haskell-lexeme-looking-at-quasi-quote-literal ()
|
||||
"Non-nil when point is just in front of Template Haskell
|
||||
quaisquote literal.
|
||||
|
||||
Quasi quotes start with '[xxx|' or '[$xxx|' sequence and end with
|
||||
'|]'. The 'xxx' is a quoter name. There is no escaping mechanism
|
||||
provided for the ending sequence.
|
||||
|
||||
Regexp has subgroup expressions:
|
||||
(match-text 1) matches the quoter name (without $ sign if present).
|
||||
(match-text 2) matches the opening vertical bar.
|
||||
(match-text 3) matches the inside of the quoted string.
|
||||
(match-text 4) matches the closing vertical bar
|
||||
or nil if at the end of the buffer.
|
||||
|
||||
Note that this function excludes 'e', 't', 'd', 'p' as quoter
|
||||
names according to Template Haskell specification."
|
||||
(let ((match-data-old (match-data)))
|
||||
(if (and
|
||||
(looking-at (rx-to-string `(: "[" (optional "$")
|
||||
(group (regexp ,haskell-lexeme-id))
|
||||
(group "|"))))
|
||||
(equal (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1)))
|
||||
'varid)
|
||||
(not (member (match-string 1) '("e" "t" "d" "p"))))
|
||||
(save-excursion
|
||||
;; note that quasi quote syntax does not have any escaping
|
||||
;; mechanism and if not closed it will span til lthe end of buffer
|
||||
(goto-char (match-end 0))
|
||||
(let ((match-data (match-data))
|
||||
(match-data-2 (and (re-search-forward "|]" nil t)
|
||||
(match-data))))
|
||||
(if match-data-2
|
||||
(set-match-data
|
||||
(list
|
||||
(nth 0 match-data) (nth 1 match-data-2) ;; whole match
|
||||
(nth 2 match-data) (nth 3 match-data) ;; quoter name
|
||||
(nth 4 match-data) (nth 5 match-data) ;; opening bar
|
||||
(nth 5 match-data) (nth 0 match-data-2) ;; inner string
|
||||
(nth 0 match-data-2) (1+ (nth 0 match-data-2)))) ;; closing bar
|
||||
|
||||
(set-match-data
|
||||
(list
|
||||
(nth 0 match-data) (point-max) ;; whole match
|
||||
(nth 2 match-data) (nth 3 match-data) ;; quoter name
|
||||
(nth 4 match-data) (nth 5 match-data) ;; opening bar
|
||||
(nth 5 match-data) (point-max) ;; inner string
|
||||
nil nil)) ;; closing bar
|
||||
))
|
||||
t)
|
||||
;; restore old match data if not matched
|
||||
(set-match-data match-data-old)
|
||||
nil)))
|
||||
|
||||
(defun haskell-lexeme-classify-by-first-char (char)
|
||||
"Classify token by CHAR.
|
||||
|
||||
CHAR is a chararacter that is assumed to be the first character
|
||||
of a token."
|
||||
(let ((category (get-char-code-property (or char ?\ ) 'general-category)))
|
||||
|
||||
(cond
|
||||
((or (member char '(?! ?# ?$ ?% ?& ?* ?+ ?. ?/ ?< ?= ?> ?? ?@ ?^ ?| ?~ ?\\ ?-))
|
||||
(and (> char 127)
|
||||
(member category '(Pc Pd Po Sm Sc Sk So))))
|
||||
'varsym)
|
||||
((equal char ?:)
|
||||
'consym)
|
||||
((equal char ?\')
|
||||
'char)
|
||||
((equal char ?\")
|
||||
'string)
|
||||
((member category '(Lu Lt))
|
||||
'conid)
|
||||
((or (equal char ?_)
|
||||
(member category '(Ll Lo)))
|
||||
'varid)
|
||||
((and (>= char ?0) (<= char ?9))
|
||||
'number)
|
||||
((member char '(?\] ?\[ ?\( ?\) ?\{ ?\} ?\` ?\, ?\;))
|
||||
'special))))
|
||||
|
||||
(defun haskell-lexeme-looking-at-token (&rest flags)
|
||||
"Like `looking-at' but understands Haskell lexemes.
|
||||
|
||||
Moves point forward over whitespace. Returns a symbol describing
|
||||
type of Haskell token recognized. Use `match-string',
|
||||
`match-beginning' and `match-end' with argument 0 to query match
|
||||
result.
|
||||
|
||||
Possible results are:
|
||||
- 'special: for chars [](){}`,;
|
||||
- 'comment: for single line comments
|
||||
- 'nested-comment: for multiline comments
|
||||
- 'qsymid: for qualified identifiers or symbols
|
||||
- 'string: for strings literals
|
||||
- 'char: for char literals
|
||||
- 'number: for decimal, float, hexadecimal and octal number literals
|
||||
- 'template-haskell-quote: for a string of apostrophes for template haskell
|
||||
- 'template-haskell-quasi-quote: for a string of apostrophes for template haskell
|
||||
|
||||
Note that for qualified symbols (match-string 1) returns the
|
||||
unqualified identifier or symbol. Further qualification for
|
||||
symbol or identifier can be done with:
|
||||
|
||||
(haskell-lexeme-classify-by-first-char (char-after (match-beginning 1)))
|
||||
|
||||
See `haskell-lexeme-classify-by-first-char' for details."
|
||||
(while
|
||||
;; Due to how unterminated strings terminate at newline, some
|
||||
;; newlines have syntax set to generic string delimeter. We want
|
||||
;; those to be treated as whitespace anyway
|
||||
(or
|
||||
(> (skip-syntax-forward "-") 0)
|
||||
(and (not (member 'newline flags))
|
||||
(> (skip-chars-forward "\n") 0))))
|
||||
(let
|
||||
((case-fold-search nil)
|
||||
(point (point-marker)))
|
||||
(or
|
||||
(and
|
||||
(equal (string-to-syntax "<")
|
||||
(get-char-property (point) 'syntax-table))
|
||||
(progn
|
||||
(set-match-data (list point (set-marker (make-marker) (line-end-position))))
|
||||
'literate-comment))
|
||||
(and (looking-at "\n")
|
||||
'newline)
|
||||
(and (looking-at "{-")
|
||||
(save-excursion
|
||||
(forward-comment 1)
|
||||
(set-match-data (list point (point-marker)))
|
||||
'nested-comment))
|
||||
(and (haskell-lexeme-looking-at-char-literal)
|
||||
'char)
|
||||
(and (haskell-lexeme-looking-at-string-literal)
|
||||
'string)
|
||||
(and (looking-at "[][(){}`,;]")
|
||||
(if (haskell-lexeme-looking-at-quasi-quote-literal)
|
||||
'template-haskell-quasi-quote
|
||||
'special))
|
||||
(and (haskell-lexeme-looking-at-qidsym)
|
||||
(if (save-match-data
|
||||
(string-match "\\`---*\\'" (match-string-no-properties 0)))
|
||||
(progn
|
||||
(set-match-data (list point (set-marker (make-marker) (line-end-position))))
|
||||
'comment)
|
||||
'qsymid))
|
||||
(and (looking-at haskell-lexeme-number)
|
||||
'number)
|
||||
(and (looking-at "'+")
|
||||
'template-haskell-quote)
|
||||
(and (looking-at ".")
|
||||
'illegal))))
|
||||
|
||||
(provide 'haskell-lexeme)
|
||||
|
||||
;;; haskell-lexeme.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-lexeme.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-lexeme.elc
Normal file
Binary file not shown.
632
elpa/haskell-mode-20171022.26/haskell-load.el
Normal file
632
elpa/haskell-mode-20171022.26/haskell-load.el
Normal file
@@ -0,0 +1,632 @@
|
||||
;;; haskell-load.el --- Compiling and loading modules in the GHCi process -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright © 2014 Chris Done. All rights reserved.
|
||||
;; 2016 Arthur Fayzrakhmanov
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-process)
|
||||
(require 'haskell-interactive-mode)
|
||||
(require 'haskell-modules)
|
||||
(require 'haskell-commands)
|
||||
(require 'haskell-session)
|
||||
(require 'haskell-string)
|
||||
|
||||
(defun haskell-process-look-config-changes (session)
|
||||
"Check whether a cabal configuration file has changed.
|
||||
Restarts the SESSION's process if that is the case."
|
||||
(let ((current-checksum (haskell-session-get session 'cabal-checksum))
|
||||
(new-checksum (haskell-cabal-compute-checksum
|
||||
(haskell-session-get session 'cabal-dir))))
|
||||
(when (not (string= current-checksum new-checksum))
|
||||
(haskell-interactive-mode-echo
|
||||
session
|
||||
(format "Cabal file changed: %s" new-checksum))
|
||||
(haskell-session-set-cabal-checksum
|
||||
session
|
||||
(haskell-session-get session 'cabal-dir))
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(unless
|
||||
(and haskell-process-prompt-restart-on-cabal-change
|
||||
(not
|
||||
(y-or-n-p "Cabal file changed. Restart GHCi process? ")))
|
||||
(haskell-process-start (haskell-interactive-session)))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))))
|
||||
|
||||
(defun haskell-process-live-build (process buffer echo-in-repl)
|
||||
"Show live updates for loading files."
|
||||
(cond
|
||||
((haskell-process-consume
|
||||
process
|
||||
(concat "\\[[ ]*\\([0-9]+\\) of \\([0-9]+\\)\\]"
|
||||
" Compiling \\([^ ]+\\)[ ]+"
|
||||
"( \\([^ ]+\\), \\([^ ]+\\) )[^\r\n]*[\r\n]+"))
|
||||
(haskell-process-echo-load-message process buffer echo-in-repl nil)
|
||||
t)
|
||||
((haskell-process-consume
|
||||
process
|
||||
(concat "\\[[ ]*\\([0-9]+\\) of \\([0-9]+\\)\\]"
|
||||
" Compiling \\[TH\\] \\([^ ]+\\)[ ]+"
|
||||
"( \\([^ ]+\\), \\([^ ]+\\) )[^\r\n]*[\r\n]+"))
|
||||
(haskell-process-echo-load-message process buffer echo-in-repl t)
|
||||
t)
|
||||
((haskell-process-consume
|
||||
process
|
||||
"Loading package \\([^ ]+\\) ... linking ... done.\n")
|
||||
(haskell-mode-message-line
|
||||
(format "Loading: %s"
|
||||
(match-string 1 buffer)))
|
||||
t)
|
||||
((haskell-process-consume
|
||||
process
|
||||
"^Preprocessing executables for \\(.+?\\)\\.\\.\\.")
|
||||
(let ((msg (format "Preprocessing: %s" (match-string 1 buffer))))
|
||||
(haskell-interactive-mode-echo (haskell-process-session process) msg)
|
||||
(haskell-mode-message-line msg)))
|
||||
((haskell-process-consume process "Linking \\(.+?\\) \\.\\.\\.")
|
||||
(let ((msg (format "Linking: %s" (match-string 1 buffer))))
|
||||
(haskell-interactive-mode-echo (haskell-process-session process) msg)
|
||||
(haskell-mode-message-line msg)))
|
||||
((haskell-process-consume process "\nBuilding \\(.+?\\)\\.\\.\\.")
|
||||
(let ((msg (format "Building: %s" (match-string 1 buffer))))
|
||||
(haskell-interactive-mode-echo (haskell-process-session process) msg)
|
||||
(haskell-mode-message-line msg)))
|
||||
((string-match "Collecting type info for [[:digit:]]+ module(s) \\.\\.\\."
|
||||
(haskell-process-response process)
|
||||
(haskell-process-response-cursor process))
|
||||
(haskell-mode-message-line (match-string 0 buffer))
|
||||
;; Do not consume "Ok, modules loaded" that goes before
|
||||
;; "Collecting type info...", just exit.
|
||||
nil)))
|
||||
|
||||
(defun haskell-process-load-complete (session process buffer reload module-buffer &optional cont)
|
||||
"Handle the complete loading response. BUFFER is the string of
|
||||
text being sent over the process pipe. MODULE-BUFFER is the
|
||||
actual Emacs buffer of the module being loaded."
|
||||
(when (get-buffer (format "*%s:splices*" (haskell-session-name session)))
|
||||
(with-current-buffer (haskell-interactive-mode-splices-buffer session)
|
||||
(erase-buffer)))
|
||||
(let* ((ok (cond
|
||||
((haskell-process-consume
|
||||
process
|
||||
"Ok, \\(?:[0-9]+\\) modules? loaded\\.$")
|
||||
t)
|
||||
((haskell-process-consume
|
||||
process
|
||||
"Failed, \\(?:[0-9]+\\) modules? loaded\\.$")
|
||||
nil)
|
||||
((haskell-process-consume
|
||||
process
|
||||
"Ok, modules loaded: \\(.+\\)\\.$")
|
||||
t)
|
||||
((haskell-process-consume
|
||||
process
|
||||
"Failed, modules loaded: \\(.+\\)\\.$")
|
||||
nil)
|
||||
(t
|
||||
(error (message "Unexpected response from haskell process.")))))
|
||||
(modules (haskell-process-extract-modules buffer))
|
||||
(cursor (haskell-process-response-cursor process))
|
||||
(warning-count 0))
|
||||
(haskell-process-set-response-cursor process 0)
|
||||
(haskell-check-remove-overlays module-buffer)
|
||||
(while
|
||||
(haskell-process-errors-warnings module-buffer session process buffer)
|
||||
(setq warning-count (1+ warning-count)))
|
||||
(haskell-process-set-response-cursor process cursor)
|
||||
(if (and (not reload)
|
||||
haskell-process-reload-with-fbytecode)
|
||||
(haskell-process-reload-with-fbytecode process module-buffer)
|
||||
(haskell-process-import-modules process (car modules)))
|
||||
(if ok
|
||||
(haskell-mode-message-line (if reload "Reloaded OK." "OK."))
|
||||
(haskell-interactive-mode-compile-error session "Compilation failed."))
|
||||
(when cont
|
||||
(condition-case-unless-debug e
|
||||
(funcall cont ok)
|
||||
(error (message "%S" e))
|
||||
(quit nil)))))
|
||||
|
||||
(defun haskell-process-suggest-imports (session file modules ident)
|
||||
"Suggest add missed imports to file.
|
||||
Asks user to add to SESSION's FILE missed import. MODULES is a
|
||||
list of modules where missed IDENT was found."
|
||||
(cl-assert session)
|
||||
(cl-assert file)
|
||||
(cl-assert ident)
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(let* ((process (haskell-session-process session))
|
||||
(suggested-already (haskell-process-suggested-imports process))
|
||||
(module
|
||||
(cond
|
||||
((> (length modules) 1)
|
||||
(when (y-or-n-p
|
||||
(format
|
||||
"Identifier `%s' not in scope, choose module to import?"
|
||||
ident))
|
||||
(haskell-complete-module-read "Module: " modules)))
|
||||
((= (length modules) 1)
|
||||
(let ((module (car modules)))
|
||||
(unless (member module suggested-already)
|
||||
(haskell-process-set-suggested-imports
|
||||
process
|
||||
(cons module suggested-already))
|
||||
(when (y-or-n-p
|
||||
(format "Identifier `%s' not in scope, import `%s'?"
|
||||
ident
|
||||
module))
|
||||
module)))))))
|
||||
(when module
|
||||
(haskell-process-find-file session file)
|
||||
(haskell-add-import module)))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))
|
||||
|
||||
(defun haskell-process-trigger-suggestions (session msg file line)
|
||||
"Trigger prompting to add any extension suggestions."
|
||||
(cond ((let ((case-fold-search nil))
|
||||
(or
|
||||
(and (string-match " -X\\([A-Z][A-Za-z]+\\)" msg)
|
||||
(not (string-match "\\([A-Z][A-Za-z]+\\) is deprecated" msg)))
|
||||
(string-match "Use \\([A-Z][A-Za-z]+\\) to permit this" msg)
|
||||
(string-match "Use \\([A-Z][A-Za-z]+\\) to allow" msg)
|
||||
(string-match "Use \\([A-Z][A-Za-z]+\\) to enable" msg)
|
||||
(string-match
|
||||
"Use \\([A-Z][A-Za-z]+\\) if you want to disable this"
|
||||
msg)
|
||||
(string-match "use \\([A-Z][A-Za-z]+\\)" msg)
|
||||
(string-match "You need \\([A-Z][A-Za-z]+\\)" msg)))
|
||||
(when haskell-process-suggest-language-pragmas
|
||||
(haskell-process-suggest-pragma
|
||||
session
|
||||
"LANGUAGE"
|
||||
(match-string 1 msg)
|
||||
file)))
|
||||
((string-match
|
||||
" The \\(qualified \\)?import of[ ][‘`‛]\\([^ ]+\\)['’] is redundant"
|
||||
msg)
|
||||
(when haskell-process-suggest-remove-import-lines
|
||||
(haskell-process-suggest-remove-import
|
||||
session
|
||||
file
|
||||
(match-string 2 msg)
|
||||
line)))
|
||||
((string-match "[Ww]arning: orphan instance: " msg)
|
||||
(when haskell-process-suggest-no-warn-orphans
|
||||
(haskell-process-suggest-pragma
|
||||
session
|
||||
"OPTIONS" "-fno-warn-orphans"
|
||||
file)))
|
||||
((or (string-match "against inferred type [‘`‛]\\[Char\\]['’]" msg)
|
||||
(string-match "with actual type [‘`‛]\\[Char\\]['’]" msg))
|
||||
(when haskell-process-suggest-overloaded-strings
|
||||
(haskell-process-suggest-pragma
|
||||
session
|
||||
"LANGUAGE" "OverloadedStrings"
|
||||
file)))
|
||||
((string-match "^Not in scope: .*[‘`‛]\\(.+\\)['’]$" msg)
|
||||
(let* ((match1 (match-string 1 msg))
|
||||
(ident (if (string-match "^[A-Za-z0-9_'.]+\\.\\(.+\\)$" match1)
|
||||
;; Skip qualification.
|
||||
(match-string 1 match1)
|
||||
match1)))
|
||||
(when haskell-process-suggest-hoogle-imports
|
||||
(let ((modules (haskell-process-hoogle-ident ident)))
|
||||
(haskell-process-suggest-imports session file modules ident)))
|
||||
(when haskell-process-suggest-haskell-docs-imports
|
||||
(let ((modules (haskell-process-haskell-docs-ident ident)))
|
||||
(haskell-process-suggest-imports session file modules ident)))
|
||||
(when haskell-process-suggest-hayoo-imports
|
||||
(let ((modules (haskell-process-hayoo-ident ident)))
|
||||
(haskell-process-suggest-imports session file modules ident)))))
|
||||
((string-match "^[ ]+It is a member of the hidden package [‘`‛]\\([^@\r\n]+\\).*['’].$" msg)
|
||||
(when haskell-process-suggest-add-package
|
||||
(haskell-process-suggest-add-package session msg)))))
|
||||
|
||||
(defun haskell-process-do-cabal (command)
|
||||
"Run a Cabal command."
|
||||
(let ((process (ignore-errors
|
||||
(haskell-interactive-process))))
|
||||
(cond
|
||||
((or (eq process nil)
|
||||
(let ((child (haskell-process-process process)))
|
||||
(not (equal 'run (process-status child)))))
|
||||
(message "Process is not running, so running directly.")
|
||||
(shell-command (concat "cabal " command)
|
||||
(get-buffer-create "*haskell-process-log*")
|
||||
(get-buffer-create "*haskell-process-log*"))
|
||||
(switch-to-buffer-other-window (get-buffer "*haskell-process-log*")))
|
||||
(t (haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list (haskell-interactive-session) process command 0)
|
||||
:go
|
||||
(lambda (state)
|
||||
(haskell-process-send-string
|
||||
(cadr state)
|
||||
(format haskell-process-do-cabal-format-string
|
||||
(haskell-session-cabal-dir (car state))
|
||||
(format "%s %s"
|
||||
(cl-ecase (haskell-process-type)
|
||||
('ghci haskell-process-path-cabal)
|
||||
('cabal-repl haskell-process-path-cabal)
|
||||
('cabal-new-repl haskell-process-path-cabal)
|
||||
('cabal-ghci haskell-process-path-cabal)
|
||||
('stack-ghci haskell-process-path-stack))
|
||||
(cl-caddr state)))))
|
||||
:live
|
||||
(lambda (state buffer)
|
||||
(let ((cmd (replace-regexp-in-string "^\\([a-z]+\\).*"
|
||||
"\\1"
|
||||
(cl-caddr state))))
|
||||
(cond ((or (string= cmd "build")
|
||||
(string= cmd "install"))
|
||||
(haskell-process-live-build (cadr state) buffer t))
|
||||
(t
|
||||
(haskell-process-cabal-live state buffer)))))
|
||||
:complete
|
||||
(lambda (state response)
|
||||
(let* ((process (cadr state))
|
||||
(session (haskell-process-session process))
|
||||
(message-count 0)
|
||||
(cursor (haskell-process-response-cursor process)))
|
||||
;; XXX: what the hell about the rampant code duplication?
|
||||
(haskell-process-set-response-cursor process 0)
|
||||
(while (haskell-process-errors-warnings nil session process response)
|
||||
(setq message-count (1+ message-count)))
|
||||
(haskell-process-set-response-cursor process cursor)
|
||||
(let ((msg (format "Complete: cabal %s (%s compiler messages)"
|
||||
(cl-caddr state)
|
||||
message-count)))
|
||||
(haskell-interactive-mode-echo session msg)
|
||||
(when (= message-count 0)
|
||||
(haskell-interactive-mode-echo
|
||||
session
|
||||
"No compiler messages, dumping complete output:")
|
||||
(haskell-interactive-mode-echo session response))
|
||||
(haskell-mode-message-line msg)
|
||||
(when (and haskell-notify-p
|
||||
(fboundp 'notifications-notify))
|
||||
(notifications-notify
|
||||
:title (format "*%s*" (haskell-session-name (car state)))
|
||||
:body msg
|
||||
:app-name (cl-ecase (haskell-process-type)
|
||||
('ghci haskell-process-path-cabal)
|
||||
('cabal-repl haskell-process-path-cabal)
|
||||
('cabal-new-repl haskell-process-path-cabal)
|
||||
('cabal-ghci haskell-process-path-cabal)
|
||||
('stack-ghci haskell-process-path-stack))
|
||||
:app-icon haskell-process-logo)))))))))))
|
||||
|
||||
(defun haskell-process-echo-load-message (process buffer echo-in-repl th)
|
||||
"Echo a load message."
|
||||
(let ((session (haskell-process-session process))
|
||||
(module-name (match-string 3 buffer))
|
||||
(file-name (match-string 4 buffer)))
|
||||
(haskell-interactive-show-load-message
|
||||
session
|
||||
'compiling
|
||||
module-name
|
||||
(haskell-session-strip-dir session file-name)
|
||||
echo-in-repl
|
||||
th)))
|
||||
|
||||
(defun haskell-process-extract-modules (buffer)
|
||||
"Extract the modules from the process buffer."
|
||||
(let* ((modules-string (match-string 1 buffer))
|
||||
(modules (split-string modules-string ", ")))
|
||||
(cons modules modules-string)))
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-error-face
|
||||
'((((supports :underline (:style wave)))
|
||||
:underline (:style wave :color "#dc322f"))
|
||||
(t
|
||||
:inherit error))
|
||||
"Face used for marking error lines."
|
||||
:group 'haskell-mode)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-warning-face
|
||||
'((((supports :underline (:style wave)))
|
||||
:underline (:style wave :color "#b58900"))
|
||||
(t
|
||||
:inherit warning))
|
||||
"Face used for marking warning lines."
|
||||
:group 'haskell-mode)
|
||||
|
||||
;;;###autoload
|
||||
(defface haskell-hole-face
|
||||
'((((supports :underline (:style wave)))
|
||||
:underline (:style wave :color "#6c71c4"))
|
||||
(t
|
||||
:inherit warning))
|
||||
"Face used for marking hole lines."
|
||||
:group 'haskell-mode)
|
||||
|
||||
(defvar haskell-check-error-fringe (propertize "!" 'display '(left-fringe exclamation-mark)))
|
||||
(defvar haskell-check-warning-fringe (propertize "?" 'display '(left-fringe question-mark)))
|
||||
(defvar haskell-check-hole-fringe (propertize "_" 'display '(left-fringe horizontal-bar)))
|
||||
|
||||
(defun haskell-check-overlay-p (ovl)
|
||||
(overlay-get ovl 'haskell-check))
|
||||
|
||||
(defun haskell-check-filter-overlays (xs)
|
||||
(cl-remove-if-not 'haskell-check-overlay-p xs))
|
||||
|
||||
(defun haskell-check-remove-overlays (buffer)
|
||||
(with-current-buffer buffer
|
||||
(remove-overlays (point-min) (point-max) 'haskell-check t)))
|
||||
|
||||
(defmacro haskell-with-overlay-properties (proplist ovl &rest body)
|
||||
"Evaluate BODY with names in PROPLIST bound to the values of
|
||||
correspondingly-named overlay properties of OVL."
|
||||
(let ((ovlvar (cl-gensym "OVL-")))
|
||||
`(let* ((,ovlvar ,ovl)
|
||||
,@(mapcar (lambda (p) `(,p (overlay-get ,ovlvar ',p))) proplist))
|
||||
,@body)))
|
||||
|
||||
(defun haskell-overlay-start> (o1 o2)
|
||||
(> (overlay-start o1) (overlay-start o2)))
|
||||
(defun haskell-overlay-start< (o1 o2)
|
||||
(< (overlay-start o1) (overlay-start o2)))
|
||||
|
||||
(defun haskell-first-overlay-in-if (test beg end)
|
||||
(let ((ovls (cl-remove-if-not test (overlays-in beg end))))
|
||||
(cl-first (sort (cl-copy-list ovls) 'haskell-overlay-start<))))
|
||||
|
||||
(defun haskell-last-overlay-in-if (test beg end)
|
||||
(let ((ovls (cl-remove-if-not test (overlays-in beg end))))
|
||||
(cl-first (sort (cl-copy-list ovls) 'haskell-overlay-start>))))
|
||||
|
||||
(defun haskell-error-overlay-briefly (ovl)
|
||||
(haskell-with-overlay-properties
|
||||
(haskell-msg haskell-msg-type) ovl
|
||||
(cond
|
||||
((not (eq haskell-msg-type 'warning))
|
||||
haskell-msg)
|
||||
((string-prefix-p "[Ww]arning:\n " haskell-msg)
|
||||
(cl-subseq haskell-msg 13))
|
||||
(t
|
||||
(error
|
||||
"Invariant failed: a warning message from GHC has unexpected form: %s."
|
||||
haskell-msg)))))
|
||||
|
||||
(defun haskell-goto-error-overlay (ovl)
|
||||
(cond (ovl
|
||||
(goto-char (overlay-start ovl))
|
||||
(haskell-mode-message-line (haskell-error-overlay-briefly ovl)))
|
||||
(t
|
||||
(message "No further notes from Haskell compiler."))))
|
||||
|
||||
(defun haskell-goto-first-error ()
|
||||
(interactive)
|
||||
(haskell-goto-error-overlay
|
||||
(haskell-first-overlay-in-if 'haskell-check-overlay-p
|
||||
(buffer-end 0) (buffer-end 1))))
|
||||
|
||||
(defun haskell-goto-prev-error ()
|
||||
(interactive)
|
||||
(haskell-goto-error-overlay
|
||||
(let ((ovl-at
|
||||
(cl-first (haskell-check-filter-overlays (overlays-at (point))))))
|
||||
(or (haskell-last-overlay-in-if 'haskell-check-overlay-p
|
||||
(point-min)
|
||||
(if ovl-at (overlay-start ovl-at) (point)))
|
||||
ovl-at))))
|
||||
|
||||
(defun haskell-goto-next-error ()
|
||||
(interactive)
|
||||
(haskell-goto-error-overlay
|
||||
(let ((ovl-at
|
||||
(cl-first (haskell-check-filter-overlays (overlays-at (point))))))
|
||||
(or (haskell-first-overlay-in-if
|
||||
'haskell-check-overlay-p
|
||||
(if ovl-at (overlay-end ovl-at) (point)) (point-max))
|
||||
ovl-at))))
|
||||
|
||||
(defun haskell-check-paint-overlay
|
||||
(buffer error-from-this-file-p line msg file type hole coln)
|
||||
(with-current-buffer buffer
|
||||
(let (beg end)
|
||||
(goto-char (point-min))
|
||||
;; XXX: we can avoid excess buffer walking by relying on the maybe-fact
|
||||
;; that GHC sorts error messages by line number, maybe.
|
||||
(cond
|
||||
(error-from-this-file-p
|
||||
(forward-line (1- line))
|
||||
(forward-char (1- coln))
|
||||
(setq beg (point))
|
||||
(if (eq type 'hole)
|
||||
(forward-char (length hole))
|
||||
(skip-chars-forward "^[:space:]" (line-end-position)))
|
||||
(setq end (point)))
|
||||
(t
|
||||
(setq beg (point))
|
||||
(forward-line)
|
||||
(setq end (point))))
|
||||
(let ((ovl (make-overlay beg end)))
|
||||
(overlay-put ovl 'haskell-check t)
|
||||
(overlay-put ovl 'haskell-file file)
|
||||
(overlay-put ovl 'haskell-msg msg)
|
||||
(overlay-put ovl 'haskell-msg-type type)
|
||||
(overlay-put ovl 'help-echo msg)
|
||||
(overlay-put ovl 'haskell-hole hole)
|
||||
(cl-destructuring-bind
|
||||
(face fringe)
|
||||
(cl-case type
|
||||
(warning
|
||||
(list 'haskell-warning-face haskell-check-warning-fringe))
|
||||
(hole
|
||||
(list 'haskell-hole-face haskell-check-hole-fringe))
|
||||
(error
|
||||
(list 'haskell-error-face haskell-check-error-fringe)))
|
||||
(overlay-put ovl 'before-string fringe)
|
||||
(overlay-put ovl 'face face))))))
|
||||
|
||||
(defun haskell-process-errors-warnings
|
||||
(module-buffer session process buffer &optional return-only)
|
||||
"Trigger handling type errors or warnings.
|
||||
Either prints the messages in the interactive buffer or if CONT
|
||||
is specified, passes the error onto that.
|
||||
|
||||
When MODULE-BUFFER is non-NIL, paint error overlays."
|
||||
(save-excursion
|
||||
(cond
|
||||
((haskell-process-consume
|
||||
process
|
||||
"\\(Module imports form a cycle:[ \n]+module [^ ]+ ([^)]+)[[:unibyte:][:nonascii:]]+?\\)\nFailed")
|
||||
(let ((err (match-string 1 buffer)))
|
||||
(if (string-match "module [`'‘‛]\\([^ ]+\\)['’`] (\\([^)]+\\))" err)
|
||||
(let* ((default-directory (haskell-session-current-dir session))
|
||||
(module (match-string 1 err))
|
||||
(file (match-string 2 err))
|
||||
(relative-file-name (file-relative-name file)))
|
||||
(unless return-only
|
||||
(haskell-interactive-show-load-message
|
||||
session
|
||||
'import-cycle
|
||||
module
|
||||
relative-file-name
|
||||
nil
|
||||
nil)
|
||||
(haskell-interactive-mode-compile-error
|
||||
session
|
||||
(format "%s:1:0: %s"
|
||||
relative-file-name
|
||||
err)))
|
||||
(list :file file :line 1 :col 0 :msg err :type 'error))
|
||||
t)))
|
||||
((haskell-process-consume
|
||||
process
|
||||
(concat "[\r\n]\\([A-Z]?:?[^ \r\n:][^:\n\r]+\\):\\([0-9()-:]+\\):"
|
||||
"[ \n\r]+\\([[:unibyte:][:nonascii:]]+?\\)\n[^ ]"))
|
||||
(haskell-process-set-response-cursor
|
||||
process
|
||||
(- (haskell-process-response-cursor process) 1))
|
||||
(let* ((buffer (haskell-process-response process))
|
||||
(file (match-string 1 buffer))
|
||||
(location-raw (match-string 2 buffer))
|
||||
(error-msg (match-string 3 buffer))
|
||||
(type (cond ((string-match "^[Ww]arning:" error-msg) 'warning)
|
||||
((string-match "^Splicing " error-msg) 'splice)
|
||||
(t 'error)))
|
||||
(critical (not (eq type 'warning)))
|
||||
;; XXX: extract hole information, pass down to
|
||||
;; `haskell-check-paint-overlay'
|
||||
(final-msg (format "%s:%s: %s"
|
||||
(haskell-session-strip-dir session file)
|
||||
location-raw
|
||||
error-msg))
|
||||
(location (haskell-process-parse-error
|
||||
(concat file ":" location-raw ": x")))
|
||||
(line (plist-get location :line))
|
||||
(col1 (plist-get location :col)))
|
||||
(when (and module-buffer haskell-process-show-overlays)
|
||||
(haskell-check-paint-overlay
|
||||
module-buffer
|
||||
(string= (file-truename (buffer-file-name module-buffer))
|
||||
(file-truename file))
|
||||
line error-msg file type nil col1))
|
||||
(if return-only
|
||||
(list :file file :line line :col col1 :msg error-msg :type type)
|
||||
(progn (funcall (cl-case type
|
||||
(warning 'haskell-interactive-mode-compile-warning)
|
||||
(splice 'haskell-interactive-mode-compile-splice)
|
||||
(error 'haskell-interactive-mode-compile-error))
|
||||
session final-msg)
|
||||
(when critical
|
||||
(haskell-mode-message-line final-msg))
|
||||
(haskell-process-trigger-suggestions
|
||||
session
|
||||
error-msg
|
||||
file
|
||||
line)
|
||||
t)))))))
|
||||
|
||||
(defun haskell-interactive-show-load-message (session type module-name file-name echo th)
|
||||
"Show the '(Compiling|Loading) X' message."
|
||||
(let ((msg (concat
|
||||
(cl-ecase type
|
||||
('compiling
|
||||
(if haskell-interactive-mode-include-file-name
|
||||
(format "Compiling: %s (%s)" module-name file-name)
|
||||
(format "Compiling: %s" module-name)))
|
||||
('loading (format "Loading: %s" module-name))
|
||||
('import-cycle
|
||||
(format "Module has an import cycle: %s" module-name)))
|
||||
(if th " [TH]" ""))))
|
||||
(haskell-mode-message-line msg)
|
||||
(when haskell-interactive-mode-delete-superseded-errors
|
||||
(haskell-interactive-mode-delete-compile-messages session file-name))
|
||||
(when echo
|
||||
(haskell-interactive-mode-echo session msg))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-reload-devel-main ()
|
||||
"Reload the module `DevelMain' and then run `DevelMain.update'.
|
||||
|
||||
This is for doing live update of the code of servers or GUI
|
||||
applications. Put your development version of the program in
|
||||
`DevelMain', and define `update' to auto-start the program on a
|
||||
new thread, and use the `foreign-store' package to access the
|
||||
running context across :load/:reloads in GHCi."
|
||||
(interactive)
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(with-current-buffer
|
||||
(or (get-buffer "DevelMain.hs")
|
||||
(if (y-or-n-p
|
||||
"You need to open a buffer named DevelMain.hs. Find now?")
|
||||
(ido-find-file)
|
||||
(error "No DevelMain.hs buffer.")))
|
||||
(let ((session (haskell-interactive-session)))
|
||||
(let ((process (haskell-interactive-process)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list :session session
|
||||
:process process
|
||||
:buffer (current-buffer))
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string (plist-get state ':process)
|
||||
":l DevelMain"))
|
||||
:live (lambda (state buffer)
|
||||
(haskell-process-live-build (plist-get state ':process)
|
||||
buffer
|
||||
nil))
|
||||
:complete (lambda (state response)
|
||||
(haskell-process-load-complete
|
||||
(plist-get state ':session)
|
||||
(plist-get state ':process)
|
||||
response
|
||||
nil
|
||||
(plist-get state ':buffer)
|
||||
(lambda (ok)
|
||||
(when ok
|
||||
(haskell-process-queue-without-filters
|
||||
(haskell-interactive-process)
|
||||
"DevelMain.update")
|
||||
(message "DevelMain updated."))))))))))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))
|
||||
|
||||
(provide 'haskell-load)
|
||||
;;; haskell-load.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-load.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-load.elc
Normal file
Binary file not shown.
162
elpa/haskell-mode-20171022.26/haskell-menu.el
Normal file
162
elpa/haskell-mode-20171022.26/haskell-menu.el
Normal file
@@ -0,0 +1,162 @@
|
||||
;;; haskell-menu.el --- A Haskell sessions menu -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2013 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Todo:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-compat)
|
||||
(require 'haskell-session)
|
||||
(require 'haskell-process)
|
||||
(require 'haskell-interactive-mode)
|
||||
|
||||
(defcustom haskell-menu-buffer-name "*haskell-menu*"
|
||||
"The name of the Haskell session menu buffer"
|
||||
:group 'haskell-interactive
|
||||
:type 'string)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-menu ()
|
||||
"Launch the Haskell sessions menu."
|
||||
(interactive)
|
||||
(or (get-buffer haskell-menu-buffer-name)
|
||||
(with-current-buffer (get-buffer-create haskell-menu-buffer-name)
|
||||
(haskell-menu-mode)))
|
||||
(switch-to-buffer-other-window (get-buffer haskell-menu-buffer-name))
|
||||
(haskell-menu-revert-function nil nil))
|
||||
|
||||
(defvar haskell-menu-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "n") 'next-line)
|
||||
(define-key map (kbd "p") 'previous-line)
|
||||
(define-key map (kbd "RET") 'haskell-menu-mode-ret)
|
||||
map)
|
||||
"Keymap for `haskell-menu-mode'.")
|
||||
|
||||
(define-derived-mode haskell-menu-mode special-mode "Haskell Session Menu"
|
||||
"Major mode for managing Haskell sessions.
|
||||
Each line describes one session.
|
||||
Letters do not insert themselves; instead, they are commands."
|
||||
(setq buffer-read-only t)
|
||||
(setq-local revert-buffer-function 'haskell-menu-revert-function)
|
||||
(setq truncate-lines t)
|
||||
(haskell-menu-revert-function nil t))
|
||||
|
||||
(suppress-keymap haskell-menu-mode-map t)
|
||||
|
||||
(defun haskell-menu-revert-function (_arg1 _arg2)
|
||||
"Function to refresh the display."
|
||||
(let ((buffer-read-only nil)
|
||||
(orig-line (line-number-at-pos))
|
||||
(orig-col (current-column)))
|
||||
(or (eq buffer-undo-list t)
|
||||
(setq buffer-undo-list nil))
|
||||
(erase-buffer)
|
||||
(haskell-menu-insert-menu)
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- orig-line))
|
||||
(forward-char orig-col)))
|
||||
|
||||
(defun haskell-menu-insert-menu ()
|
||||
"Insert the Haskell sessions menu to the current buffer."
|
||||
(if (null haskell-sessions)
|
||||
(insert "No Haskell sessions.")
|
||||
(haskell-menu-tabulate
|
||||
(list "Name" "PID" "Time" "RSS" "Cabal directory" "Working directory" "Command")
|
||||
(mapcar (lambda (session)
|
||||
(let ((process (haskell-process-process (haskell-session-process session))))
|
||||
(cond
|
||||
(process
|
||||
(let ((id (process-id process)))
|
||||
(list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer)
|
||||
(if (process-live-p process) (number-to-string id) "-")
|
||||
(if (process-live-p process)
|
||||
(format-time-string "%H:%M:%S"
|
||||
(encode-time (cl-caddr (assoc 'etime (process-attributes id)))
|
||||
0 0 0 0 0))
|
||||
"-")
|
||||
(if (process-live-p process)
|
||||
(concat (number-to-string (/ (cdr (assoc 'rss (process-attributes id)))
|
||||
1024))
|
||||
"MB")
|
||||
"-")
|
||||
(haskell-session-cabal-dir session)
|
||||
(haskell-session-current-dir session)
|
||||
(mapconcat 'identity (process-command process) " "))))
|
||||
(t (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer)
|
||||
"—"
|
||||
"—"
|
||||
"—"
|
||||
(haskell-session-cabal-dir session)
|
||||
(haskell-session-current-dir session))))))
|
||||
haskell-sessions))))
|
||||
|
||||
(defun haskell-menu-tabulate (headings rows)
|
||||
"Prints a list of lists as a formatted table to the current buffer."
|
||||
(let* ((columns (length headings))
|
||||
(widths (make-list columns 0)))
|
||||
;; Calculate column widths. This is kind of hideous.
|
||||
(dolist (row rows)
|
||||
(setq widths
|
||||
(let ((list (list)))
|
||||
(dotimes (i columns)
|
||||
(setq list (cons (max (nth i widths)
|
||||
(1+ (length (nth i row)))
|
||||
(1+ (length (nth i headings))))
|
||||
list)))
|
||||
(reverse list))))
|
||||
;; Print headings.
|
||||
(let ((heading (propertize " " 'display '(space :align-to 0))))
|
||||
(dotimes (i columns)
|
||||
(setq heading (concat heading
|
||||
(format (concat "%-" (number-to-string (nth i widths)) "s")
|
||||
(nth i headings)))))
|
||||
(setq header-line-format heading))
|
||||
;; Print tabulated rows.
|
||||
(dolist (row rows)
|
||||
(dotimes (i columns)
|
||||
(insert (format (concat "%-" (number-to-string (nth i widths)) "s")
|
||||
(nth i row))))
|
||||
(insert "\n"))))
|
||||
|
||||
(defun haskell-menu-mode-ret ()
|
||||
"Handle RET key."
|
||||
(interactive)
|
||||
(let* ((name (save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(buffer-substring-no-properties (point)
|
||||
(progn (search-forward " ")
|
||||
(forward-char -1)
|
||||
(point)))))
|
||||
(session (car (cl-remove-if-not (lambda (session)
|
||||
(string= (haskell-session-name session)
|
||||
name))
|
||||
haskell-sessions))))
|
||||
(switch-to-buffer (haskell-session-interactive-buffer session))))
|
||||
|
||||
(provide 'haskell-menu)
|
||||
|
||||
;;; haskell-menu.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-menu.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-menu.elc
Normal file
Binary file not shown.
1012
elpa/haskell-mode-20171022.26/haskell-mode-autoloads.el
Normal file
1012
elpa/haskell-mode-20171022.26/haskell-mode-autoloads.el
Normal file
File diff suppressed because it is too large
Load Diff
8
elpa/haskell-mode-20171022.26/haskell-mode-pkg.el
Normal file
8
elpa/haskell-mode-20171022.26/haskell-mode-pkg.el
Normal file
@@ -0,0 +1,8 @@
|
||||
(define-package "haskell-mode" "20171022.26" "A Haskell editing mode"
|
||||
'((emacs "24.3"))
|
||||
:keywords
|
||||
'("haskell" "cabal" "ghc" "repl")
|
||||
:url "https://github.com/haskell/haskell-mode")
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; End:
|
||||
1198
elpa/haskell-mode-20171022.26/haskell-mode.el
Normal file
1198
elpa/haskell-mode-20171022.26/haskell-mode.el
Normal file
File diff suppressed because it is too large
Load Diff
BIN
elpa/haskell-mode-20171022.26/haskell-mode.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-mode.elc
Normal file
Binary file not shown.
2422
elpa/haskell-mode-20171022.26/haskell-mode.info
Normal file
2422
elpa/haskell-mode-20171022.26/haskell-mode.info
Normal file
File diff suppressed because it is too large
Load Diff
117
elpa/haskell-mode-20171022.26/haskell-modules.el
Normal file
117
elpa/haskell-mode-20171022.26/haskell-modules.el
Normal file
@@ -0,0 +1,117 @@
|
||||
;;; haskell-modules.el --- -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'haskell-sort-imports)
|
||||
(require 'haskell-align-imports)
|
||||
(require 'haskell-session)
|
||||
(require 'haskell-navigate-imports)
|
||||
(require 'haskell-complete-module)
|
||||
(require 'haskell-sandbox)
|
||||
(require 'haskell-customize)
|
||||
|
||||
(defun haskell-add-import (&optional module)
|
||||
"Add an import to the import list. Sorts and aligns imports,
|
||||
unless `haskell-stylish-on-save' is set, in which case we defer
|
||||
to stylish-haskell."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(haskell-navigate-imports)
|
||||
(insert (haskell-import-for-module
|
||||
(or module
|
||||
(haskell-complete-module-read
|
||||
"Module: "
|
||||
(haskell-session-all-modules (haskell-modules-session))))))
|
||||
(unless haskell-stylish-on-save (haskell-sort-imports)
|
||||
(haskell-align-imports))))
|
||||
|
||||
(defun haskell-import-for-module (module)
|
||||
"Get import statements for the given module."
|
||||
(let ((mapping (assoc module haskell-import-mapping)))
|
||||
(if mapping
|
||||
(cdr mapping)
|
||||
(concat (read-from-minibuffer "Import line: "
|
||||
(format "import %s" module))
|
||||
"\n"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-installed-modules (_session &optional _dontcreate)
|
||||
"Get the modules installed in the current package set."
|
||||
;; TODO: Again, this makes HEAVY use of unix utilities. It'll work
|
||||
;; fine in Linux, probably okay on OS X, and probably not at all on
|
||||
;; Windows. Again, if someone wants to test on Windows and come up
|
||||
;; with alternatives that's OK.
|
||||
;;
|
||||
;; Ideally all these package queries can be provided by a Haskell
|
||||
;; program based on the Cabal API. Possibly as a nice service. Such
|
||||
;; a service could cache and do nice things like that. For now, this
|
||||
;; simple shell script takes us far.
|
||||
;;
|
||||
;; Probably also we can take the code from inferior-haskell-mode.
|
||||
;;
|
||||
;; Ugliness aside, if it saves us time to type it's a winner.
|
||||
;;
|
||||
;; FIXME/TODO: add support for (eq 'cabal-repl (haskell-process-type))
|
||||
(let ((session (haskell-session-maybe)))
|
||||
(when session
|
||||
(let ((modules (shell-command-to-string
|
||||
(format "%s 2> /dev/null | %s | %s"
|
||||
(cond
|
||||
((haskell-sandbox-exists-p session)
|
||||
(concat "ghc-pkg dump -f "
|
||||
(shell-quote-argument (haskell-sandbox-pkgdb session))))
|
||||
(t "ghc-pkg dump"))
|
||||
"egrep '^(exposed-modules: | )[A-Z]'"
|
||||
"cut -c18-"))))
|
||||
(split-string modules)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-all-modules (session &optional dontcreate)
|
||||
"Get all modules -- installed or in the current project.
|
||||
If DONTCREATE is non-nil don't create a new session."
|
||||
(append (haskell-session-installed-modules session dontcreate)
|
||||
(haskell-session-project-modules session dontcreate)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-project-modules (session &optional dontcreate)
|
||||
"Get the modules of the current project.
|
||||
If DONTCREATE is non-nil don't create a new session."
|
||||
(if (or (not dontcreate) (haskell-session-maybe))
|
||||
(let* ((modules
|
||||
(shell-command-to-string
|
||||
(format "%s && %s"
|
||||
(format "cd %s" (haskell-session-cabal-dir session))
|
||||
;; TODO: Use a different, better source. Possibly hasktags or some such.
|
||||
;; TODO: At least make it cross-platform. Linux
|
||||
;; (and possibly OS X) have egrep, Windows
|
||||
;; doesn't -- or does it via Cygwin or MinGW?
|
||||
;; This also doesn't handle module\nName. But those gits can just cut it out!
|
||||
"egrep '^module[\t\r ]+[^(\t\r ]+' . -r -I --include='*.*hs' --include='*.hsc' -s -o -h | sed 's/^module[\t\r ]*//' | sort | uniq"))))
|
||||
(split-string modules))))
|
||||
|
||||
(defun haskell-modules-session ()
|
||||
"Get the `haskell-session', throw an error if it's not
|
||||
available."
|
||||
(or (haskell-session-maybe)
|
||||
(haskell-session-assign
|
||||
(or (haskell-session-from-buffer)
|
||||
(haskell-session-choose)
|
||||
(error "No session associated with this buffer. Try M-x haskell-session-change or report this as a bug.")))))
|
||||
|
||||
(provide 'haskell-modules)
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-modules.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-modules.elc
Normal file
Binary file not shown.
130
elpa/haskell-mode-20171022.26/haskell-move-nested.el
Normal file
130
elpa/haskell-mode-20171022.26/haskell-move-nested.el
Normal file
@@ -0,0 +1,130 @@
|
||||
;;; haskell-move-nested.el --- Change the column of text nested below a line -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2010 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This program is free software: you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation, either version 3 of
|
||||
;; the License, or (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be
|
||||
;; useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
;; PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public
|
||||
;; License along with this program. If not, see
|
||||
;; <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This module is intended for Haskell mode users, but is
|
||||
;; independent of Haskell mode.
|
||||
|
||||
;; Example usage:
|
||||
|
||||
;; (define-key haskell-mode-map (kbd "C-,") 'haskell-move-nested-left)
|
||||
;; (define-key haskell-mode-map (kbd "C-.") 'haskell-move-nested-right)
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-move-nested (cols)
|
||||
"Shift the nested off-side-rule block adjacent to point by COLS columns to the right.
|
||||
|
||||
In Transient Mark mode, if the mark is active, operate on the contents
|
||||
of the region instead.
|
||||
"
|
||||
(save-excursion
|
||||
(if (and transient-mark-mode mark-active)
|
||||
(progn
|
||||
(indent-rigidly (region-beginning) (region-end) cols)
|
||||
(setq deactivate-mark nil))
|
||||
(let ((region (haskell-move-nested-region)))
|
||||
(when region
|
||||
(indent-rigidly (car region) (cdr region) cols))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-move-nested-right (cols)
|
||||
"Increase indentation of the following off-side-rule block adjacent to point.
|
||||
|
||||
Use a numeric prefix argument to indicate amount of indentation to apply.
|
||||
|
||||
In Transient Mark mode, if the mark is active, operate on the contents
|
||||
of the region instead."
|
||||
(interactive "p")
|
||||
(haskell-move-nested cols)
|
||||
)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-move-nested-left (cols)
|
||||
"Decrease indentation of the following off-side-rule block adjacent to point.
|
||||
|
||||
Use a numeric prefix argument to indicate amount of indentation to apply.
|
||||
|
||||
In Transient Mark mode, if the mark is active, operate on the contents
|
||||
of the region instead."
|
||||
(interactive "p")
|
||||
(haskell-move-nested (- cols))
|
||||
)
|
||||
|
||||
(defun haskell-move-nested-region ()
|
||||
"Infer region off-side-rule block adjacent to point.
|
||||
Used by `haskell-move-nested'.
|
||||
"
|
||||
(save-excursion
|
||||
(let ((starting-level (current-column)))
|
||||
(forward-line)
|
||||
(let ((current-level (haskell-move-nested-indent-level)))
|
||||
(let ((start-point (line-beginning-position))
|
||||
(start-end-point (line-end-position))
|
||||
(end-point nil)
|
||||
(last-line 0))
|
||||
(forward-line)
|
||||
(while (and (not (= (line-beginning-position) last-line))
|
||||
(or (> (haskell-move-nested-indent-level) starting-level)
|
||||
(and (> current-level starting-level)
|
||||
(>= (haskell-move-nested-indent-level) current-level))))
|
||||
(setq last-line (line-beginning-position))
|
||||
(setq end-point (line-end-position))
|
||||
(forward-line))
|
||||
(cons start-point (or end-point
|
||||
start-end-point)))))))
|
||||
|
||||
(defun haskell-move-nested-indent-level ()
|
||||
(max
|
||||
0
|
||||
(1- (length
|
||||
(buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(or (save-excursion (goto-char (line-beginning-position))
|
||||
(search-forward-regexp "[^ ]" (line-end-position) t 1))
|
||||
(line-beginning-position)))))))
|
||||
|
||||
(defun haskell-kill-nested ()
|
||||
"Kill the nested region after point."
|
||||
(interactive)
|
||||
(let ((start (point))
|
||||
(reg (save-excursion
|
||||
(search-backward-regexp "^[ ]+" (line-beginning-position) t 1)
|
||||
(search-forward-regexp "[^ ]" (line-end-position) t 1)
|
||||
(haskell-move-nested-region))))
|
||||
(kill-region start (cdr reg))))
|
||||
|
||||
(defun haskell-delete-nested ()
|
||||
"Kill the nested region after point."
|
||||
(interactive)
|
||||
(let ((start (point))
|
||||
(reg (save-excursion
|
||||
(search-backward-regexp "^[ ]+" (line-beginning-position) t 1)
|
||||
(search-forward-regexp "[^ ]" (line-end-position) t 1)
|
||||
(haskell-move-nested-region))))
|
||||
(delete-region start (cdr reg))))
|
||||
|
||||
(provide 'haskell-move-nested)
|
||||
|
||||
;;; haskell-move-nested.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-move-nested.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-move-nested.elc
Normal file
Binary file not shown.
122
elpa/haskell-mode-20171022.26/haskell-navigate-imports.el
Normal file
122
elpa/haskell-mode-20171022.26/haskell-navigate-imports.el
Normal file
@@ -0,0 +1,122 @@
|
||||
;;; haskell-navigate-imports.el --- A function for cycling through Haskell import lists -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2010 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This program is free software: you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation, either version 3 of
|
||||
;; the License, or (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be
|
||||
;; useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
;; PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public
|
||||
;; License along with this program. If not, see
|
||||
;; <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; The cycling step will stop once at the last import list so
|
||||
;; that it is easy to add a new import list.
|
||||
|
||||
;; This module works completely independently of any libraries
|
||||
;; (including haskell-mode).
|
||||
|
||||
;; Exports three interactive functions:
|
||||
;; 1. haskell-navigate-imports
|
||||
;; 2. haskell-navigate-imports-go
|
||||
;; 3. haskell-navigate-imports-return
|
||||
|
||||
;; Example usage:
|
||||
|
||||
;; (require 'haskell-navigate-imports)
|
||||
;; (define-key haskell-mode-map (kbd "<f8>") 'haskell-navigate-imports)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defvar haskell-navigate-imports-start-point nil)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-navigate-imports (&optional return)
|
||||
"Cycle the Haskell import lines or return to point (with prefix arg)."
|
||||
(interactive "P")
|
||||
(if return
|
||||
(haskell-navigate-imports-return)
|
||||
(haskell-navigate-imports-go)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-navigate-imports-go ()
|
||||
"Go to the first line of a list of consecutive import lines. Cycles."
|
||||
(interactive)
|
||||
(unless (or (haskell-navigate-imports-line)
|
||||
(equal (line-beginning-position) (point-min))
|
||||
(save-excursion (forward-line -1)
|
||||
(haskell-navigate-imports-line)))
|
||||
(setq haskell-navigate-imports-start-point (point)))
|
||||
(haskell-navigate-imports-go-internal))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-navigate-imports-return ()
|
||||
"Return to the non-import point we were at before going to the module list.
|
||||
If we were originally at an import list, we can just cycle through easily."
|
||||
(interactive)
|
||||
(when haskell-navigate-imports-start-point
|
||||
(goto-char haskell-navigate-imports-start-point)))
|
||||
|
||||
(defun haskell-navigate-imports-go-internal ()
|
||||
"Go to the first line of a list of consecutive import lines. Cycle."
|
||||
(if (haskell-navigate-imports-line)
|
||||
(progn (haskell-navigate-imports-goto-end)
|
||||
(when (haskell-navigate-imports-find-forward-line)
|
||||
(haskell-navigate-imports-go-internal)))
|
||||
(let ((point (haskell-navigate-imports-find-forward-line)))
|
||||
(if point
|
||||
(goto-char point)
|
||||
(progn (goto-char (point-min))
|
||||
(if (haskell-navigate-imports-find-forward-line)
|
||||
(haskell-navigate-imports-go-internal)
|
||||
(when (search-forward-regexp "^module" nil t 1)
|
||||
(search-forward "\n\n" nil t 1))))))))
|
||||
|
||||
(defun haskell-navigate-imports-goto-end ()
|
||||
"Skip a bunch of consecutive import lines."
|
||||
(while (not (or (equal (point)
|
||||
(point-max))
|
||||
(not (haskell-navigate-imports-line))))
|
||||
(forward-line)))
|
||||
|
||||
(defun haskell-navigate-imports-find-forward-line ()
|
||||
"Return a point with at an import line, or nothing."
|
||||
(save-excursion
|
||||
(while (not (or (equal (point) (point-max))
|
||||
(haskell-navigate-imports-after-imports-p) ;; This one just speeds it up.
|
||||
(haskell-navigate-imports-line)))
|
||||
(forward-line))
|
||||
(if (haskell-navigate-imports-line)
|
||||
(point)
|
||||
nil)))
|
||||
|
||||
(defun haskell-navigate-imports-line ()
|
||||
"Try to match the current line as a regexp."
|
||||
(let ((line (buffer-substring-no-properties (line-beginning-position)
|
||||
(line-end-position))))
|
||||
(if (string-match "^import " line)
|
||||
line
|
||||
nil)))
|
||||
|
||||
(defun haskell-navigate-imports-after-imports-p ()
|
||||
"Are we after the imports list? Just for a speed boost."
|
||||
(save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(not (not (search-forward-regexp "\\( = \\|\\<instance\\>\\| :: \\)"
|
||||
(line-end-position) t 1)))))
|
||||
|
||||
(provide 'haskell-navigate-imports)
|
||||
|
||||
;;; haskell-navigate-imports.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-navigate-imports.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-navigate-imports.elc
Normal file
Binary file not shown.
104
elpa/haskell-mode-20171022.26/haskell-presentation-mode.el
Normal file
104
elpa/haskell-mode-20171022.26/haskell-presentation-mode.el
Normal file
@@ -0,0 +1,104 @@
|
||||
;;; haskell-presentation-mode.el --- Presenting Haskell things -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2013 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-session)
|
||||
|
||||
(defvar haskell-presentation-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "q") 'quit-window)
|
||||
(define-key map (kbd "c") 'haskell-presentation-clear)
|
||||
map)
|
||||
"Keymap for `haskell-presentation-mode'.")
|
||||
|
||||
(define-derived-mode haskell-presentation-mode
|
||||
haskell-mode "Presentation"
|
||||
"Major mode for viewing Haskell snippets.
|
||||
\\{hypertext-mode-map}"
|
||||
(setq case-fold-search nil))
|
||||
|
||||
(defconst haskell-presentation-buffer-name
|
||||
"*Haskell Presentation*"
|
||||
"Haskell Presentation buffer name.")
|
||||
|
||||
(defconst haskell-presentation-hint-message
|
||||
"-- Hit `q' to close this window; `c' to clear.\n\n"
|
||||
"Hint message appered in Haskell Presentation buffer.")
|
||||
|
||||
(defun haskell-presentation-buffer ()
|
||||
"Return Haskell Presentaion buffer.
|
||||
Return current presenation buffer or create new one if absent.
|
||||
Never returns nil."
|
||||
;; TODO Provide interactive calling options: when called interactively make
|
||||
;; the presentation buffer current.
|
||||
(let ((may-buffer (get-buffer haskell-presentation-buffer-name)))
|
||||
(if may-buffer
|
||||
may-buffer
|
||||
(let ((buffer (generate-new-buffer haskell-presentation-buffer-name)))
|
||||
(with-current-buffer buffer
|
||||
(insert haskell-presentation-hint-message)
|
||||
(haskell-presentation-mode)
|
||||
(setq buffer-read-only t))
|
||||
buffer))))
|
||||
|
||||
(defun haskell-presentation-clear ()
|
||||
"Clear Haskell Presentation buffer."
|
||||
(interactive)
|
||||
(let ((hp-buf (get-buffer haskell-presentation-buffer-name)))
|
||||
(when hp-buf
|
||||
(with-current-buffer hp-buf
|
||||
(let ((buffer-read-only nil))
|
||||
(erase-buffer)
|
||||
(insert haskell-presentation-hint-message))))))
|
||||
|
||||
(defun haskell-presentation-present (session code &optional clear)
|
||||
"Present given code in a popup buffer.
|
||||
Creates temporal Haskell Presentation buffer and assigns it to
|
||||
given haskell SESSION; presented CODE will be fontified as
|
||||
haskell code. Give an optional non-nil CLEAR arg to clear the
|
||||
buffer before presenting message."
|
||||
(let ((buffer (haskell-presentation-buffer)))
|
||||
(with-current-buffer buffer
|
||||
|
||||
(when (boundp 'shm-display-quarantine)
|
||||
(setq-local shm-display-quarantine nil))
|
||||
|
||||
(when clear (haskell-presentation-clear))
|
||||
(haskell-session-assign session)
|
||||
(goto-char (point-min))
|
||||
(forward-line 2)
|
||||
(save-excursion
|
||||
(let ((buffer-read-only nil))
|
||||
(insert code "\n\n"))))
|
||||
|
||||
(if (eq major-mode 'haskell-presentation-mode)
|
||||
(switch-to-buffer buffer)
|
||||
(pop-to-buffer buffer))))
|
||||
|
||||
(provide 'haskell-presentation-mode)
|
||||
|
||||
;;; haskell-presentation-mode.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-presentation-mode.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-presentation-mode.elc
Normal file
Binary file not shown.
510
elpa/haskell-mode-20171022.26/haskell-process.el
Normal file
510
elpa/haskell-mode-20171022.26/haskell-process.el
Normal file
@@ -0,0 +1,510 @@
|
||||
;;; haskell-process.el --- Communicating with the inferior Haskell process -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2011 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'json)
|
||||
(require 'url-util)
|
||||
(require 'haskell-compat)
|
||||
(require 'haskell-session)
|
||||
(require 'haskell-customize)
|
||||
(require 'haskell-string)
|
||||
|
||||
(defconst haskell-process-prompt-regex "\4"
|
||||
"Used for delimiting command replies. 4 is End of Transmission.")
|
||||
|
||||
(defvar haskell-reload-p nil
|
||||
"Used internally for `haskell-process-loadish'.")
|
||||
|
||||
(defconst haskell-process-greetings
|
||||
(list "Hello, Haskell!"
|
||||
"The lambdas must flow."
|
||||
"Hours of hacking await!"
|
||||
"The next big Haskell project is about to start!"
|
||||
"Your wish is my IO ().")
|
||||
"Greetings for when the Haskell process starts up.")
|
||||
|
||||
(defconst haskell-process-logo
|
||||
(expand-file-name "logo.svg" haskell-mode-pkg-base-dir)
|
||||
"Haskell logo for notifications.")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Accessing commands -- using cl 'defstruct'
|
||||
|
||||
(cl-defstruct haskell-command
|
||||
"Data structure representing a command to be executed when with
|
||||
a custom state and three callback."
|
||||
;; hold the custom command state
|
||||
;; state :: a
|
||||
state
|
||||
;; called when to execute a command
|
||||
;; go :: a -> ()
|
||||
go
|
||||
;; called whenever output was collected from the haskell process
|
||||
;; live :: a -> Response -> Bool
|
||||
live
|
||||
;; called when the output from the haskell process indicates that the command
|
||||
;; is complete
|
||||
;; complete :: a -> Response -> ()
|
||||
complete)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Building the process
|
||||
|
||||
(defun haskell-process-compute-process-log-and-command (session hptype)
|
||||
"Compute the log and process to start command for the SESSION from the HPTYPE.
|
||||
Do not actually start any process.
|
||||
HPTYPE is the result of calling `'haskell-process-type`' function."
|
||||
(let ((session-name (haskell-session-name session)))
|
||||
(cl-ecase hptype
|
||||
('ghci
|
||||
(append (list (format "Starting inferior GHCi process %s ..."
|
||||
haskell-process-path-ghci)
|
||||
session-name
|
||||
nil)
|
||||
(apply haskell-process-wrapper-function
|
||||
(list
|
||||
(append (haskell-process-path-to-list haskell-process-path-ghci)
|
||||
haskell-process-args-ghci)))))
|
||||
('cabal-new-repl
|
||||
(append (list (format "Starting inferior `cabal new-repl' process using %s ..."
|
||||
haskell-process-path-cabal)
|
||||
session-name
|
||||
nil)
|
||||
(apply haskell-process-wrapper-function
|
||||
(list
|
||||
(append
|
||||
(haskell-process-path-to-list haskell-process-path-cabal)
|
||||
(list "new-repl")
|
||||
haskell-process-args-cabal-new-repl
|
||||
(let ((target (haskell-session-target session)))
|
||||
(if target (list target) nil)))))))
|
||||
('cabal-repl
|
||||
(append (list (format "Starting inferior `cabal repl' process using %s ..."
|
||||
haskell-process-path-cabal)
|
||||
session-name
|
||||
nil)
|
||||
(apply haskell-process-wrapper-function
|
||||
(list
|
||||
(append
|
||||
(haskell-process-path-to-list haskell-process-path-cabal)
|
||||
(list "repl")
|
||||
haskell-process-args-cabal-repl
|
||||
(let ((target (haskell-session-target session)))
|
||||
(if target (list target) nil)))))))
|
||||
('stack-ghci
|
||||
(append (list (format "Starting inferior stack GHCi process using %s" haskell-process-path-stack)
|
||||
session-name
|
||||
nil)
|
||||
(apply haskell-process-wrapper-function
|
||||
(list
|
||||
(append
|
||||
(haskell-process-path-to-list haskell-process-path-stack)
|
||||
(list "ghci")
|
||||
(let ((target (haskell-session-target session)))
|
||||
(if target (list target) nil))
|
||||
haskell-process-args-stack-ghci))))))))
|
||||
|
||||
(defun haskell-process-path-to-list (path)
|
||||
"Convert a path (which may be a string or a list) to a list."
|
||||
(if (stringp path)
|
||||
(list path)
|
||||
path))
|
||||
|
||||
(defun haskell-process-make (name)
|
||||
"Make an inferior Haskell process."
|
||||
(list (cons 'name name)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Process communication
|
||||
|
||||
(defun haskell-process-sentinel (proc event)
|
||||
"The sentinel for the process pipe."
|
||||
(let ((session (haskell-process-project-by-proc proc)))
|
||||
(when session
|
||||
(let* ((process (haskell-session-process session)))
|
||||
(unless (haskell-process-restarting process)
|
||||
(haskell-process-log
|
||||
(propertize (format "Event: %S\n" event)
|
||||
'face '((:weight bold))))
|
||||
(haskell-process-log
|
||||
(propertize "Process reset.\n"
|
||||
'face 'font-lock-comment-face))
|
||||
(run-hook-with-args 'haskell-process-ended-functions process))))))
|
||||
|
||||
(defun haskell-process-filter (proc response)
|
||||
"The filter for the process pipe."
|
||||
(let ((i 0))
|
||||
(cl-loop for line in (split-string response "\n")
|
||||
do (haskell-process-log
|
||||
(concat (if (= i 0)
|
||||
(propertize "<- " 'face 'font-lock-comment-face)
|
||||
" ")
|
||||
(propertize line 'face 'haskell-interactive-face-compile-warning)))
|
||||
do (setq i (1+ i))))
|
||||
(let ((session (haskell-process-project-by-proc proc)))
|
||||
(when session
|
||||
(if (haskell-process-cmd (haskell-session-process session))
|
||||
(haskell-process-collect session
|
||||
response
|
||||
(haskell-session-process session))))))
|
||||
|
||||
(defun haskell-process-log (msg)
|
||||
"Effective append MSG to the process log (if enabled)."
|
||||
(when haskell-process-log
|
||||
(let* ((append-to (get-buffer-create "*haskell-process-log*")))
|
||||
(with-current-buffer append-to
|
||||
;; point should follow insertion so that it stays at the end
|
||||
;; of the buffer
|
||||
(setq-local window-point-insertion-type t)
|
||||
(let ((buffer-read-only nil))
|
||||
(insert msg "\n"))))))
|
||||
|
||||
(defun haskell-process-project-by-proc (proc)
|
||||
"Find project by process."
|
||||
(cl-find-if (lambda (project)
|
||||
(string= (haskell-session-name project)
|
||||
(process-name proc)))
|
||||
haskell-sessions))
|
||||
|
||||
(defun haskell-process-collect (_session response process)
|
||||
"Collect input for the response until receives a prompt."
|
||||
(haskell-process-set-response process
|
||||
(concat (haskell-process-response process) response))
|
||||
(while (haskell-process-live-updates process))
|
||||
(when (string-match haskell-process-prompt-regex
|
||||
(haskell-process-response process))
|
||||
(haskell-command-exec-complete
|
||||
(haskell-process-cmd process)
|
||||
(replace-regexp-in-string
|
||||
haskell-process-prompt-regex
|
||||
""
|
||||
(haskell-process-response process)))
|
||||
(haskell-process-reset process)
|
||||
(haskell-process-trigger-queue process)))
|
||||
|
||||
(defun haskell-process-reset (process)
|
||||
"Reset the process's state, ready for the next send/reply."
|
||||
(progn (haskell-process-set-response-cursor process 0)
|
||||
(haskell-process-set-response process "")
|
||||
(haskell-process-set-cmd process nil)))
|
||||
|
||||
(defun haskell-process-consume (process regex)
|
||||
"Consume a regex from the response and move the cursor along if succeed."
|
||||
(when (string-match regex
|
||||
(haskell-process-response process)
|
||||
(haskell-process-response-cursor process))
|
||||
(haskell-process-set-response-cursor process (match-end 0))
|
||||
t))
|
||||
|
||||
(defun haskell-process-send-string (process string)
|
||||
"Try to send a string to the process's process. Ask to restart if it's not running."
|
||||
(let ((child (haskell-process-process process)))
|
||||
(if (equal 'run (process-status child))
|
||||
(let ((out (concat string "\n")))
|
||||
(let ((i 0))
|
||||
(cl-loop for line in (split-string out "\n")
|
||||
do (unless (string-equal "" line)
|
||||
(haskell-process-log
|
||||
(concat (if (= i 0)
|
||||
(propertize "-> " 'face 'font-lock-comment-face)
|
||||
" ")
|
||||
(propertize line 'face 'font-lock-string-face))))
|
||||
do (setq i (1+ i))))
|
||||
(process-send-string child out))
|
||||
(unless (haskell-process-restarting process)
|
||||
(run-hook-with-args 'haskell-process-ended-functions process)))))
|
||||
|
||||
(defun haskell-process-live-updates (process)
|
||||
"Process live updates."
|
||||
(haskell-command-exec-live (haskell-process-cmd process)
|
||||
(haskell-process-response process)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Making commands
|
||||
|
||||
(defun haskell-process-queue-without-filters (process line)
|
||||
"Queue LINE to be sent to PROCESS without bothering to look at
|
||||
the response."
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (cons process line)
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string (car state)
|
||||
(cdr state))))))
|
||||
|
||||
|
||||
(defun haskell-process-queue-command (process command)
|
||||
"Add a command to the process command queue."
|
||||
(haskell-process-cmd-queue-add process command)
|
||||
(haskell-process-trigger-queue process))
|
||||
|
||||
(defun haskell-process-trigger-queue (process)
|
||||
"Trigger the next command in the queue to be ran if there is no current command."
|
||||
(if (and (haskell-process-process process)
|
||||
(process-live-p (haskell-process-process process)))
|
||||
(unless (haskell-process-cmd process)
|
||||
(let ((cmd (haskell-process-cmd-queue-pop process)))
|
||||
(when cmd
|
||||
(haskell-process-set-cmd process cmd)
|
||||
(haskell-command-exec-go cmd))))
|
||||
(progn (haskell-process-reset process)
|
||||
(haskell-process-set process 'command-queue nil)
|
||||
(run-hook-with-args 'haskell-process-ended-functions process))))
|
||||
|
||||
(defun haskell-process-queue-flushed-p (process)
|
||||
"Return t if command queue has been completely processed."
|
||||
(not (or (haskell-process-cmd-queue process)
|
||||
(haskell-process-cmd process))))
|
||||
|
||||
(defun haskell-process-queue-flush (process)
|
||||
"Block till PROCESS' command queue has been completely processed.
|
||||
This uses `accept-process-output' internally."
|
||||
(while (not (haskell-process-queue-flushed-p process))
|
||||
(haskell-process-trigger-queue process)
|
||||
(accept-process-output (haskell-process-process process) 1)))
|
||||
|
||||
(defun haskell-process-queue-sync-request (process reqstr)
|
||||
"Queue submitting REQSTR to PROCESS and return response blockingly."
|
||||
(let ((cmd (make-haskell-command
|
||||
:state (cons nil process)
|
||||
:go `(lambda (s) (haskell-process-send-string (cdr s) ,reqstr))
|
||||
:complete 'setcar)))
|
||||
(haskell-process-queue-command process cmd)
|
||||
(haskell-process-queue-flush process)
|
||||
(car-safe (haskell-command-state cmd))))
|
||||
|
||||
(defun haskell-process-get-repl-completions (process inputstr &optional limit)
|
||||
"Query PROCESS with `:complete repl ...' for INPUTSTR.
|
||||
Give optional LIMIT arg to limit completion candidates count,
|
||||
zero, negative values, and nil means all possible completions.
|
||||
Returns NIL when no completions found."
|
||||
(let* ((mlimit (if (and limit (> limit 0))
|
||||
(concat " " (number-to-string limit) " ")
|
||||
" "))
|
||||
(reqstr (concat ":complete repl"
|
||||
mlimit
|
||||
(haskell-string-literal-encode inputstr)))
|
||||
(rawstr (haskell-process-queue-sync-request process reqstr))
|
||||
(response-status (haskell-utils-repl-response-error-status rawstr)))
|
||||
(if (eq 'unknown-command response-status)
|
||||
(error
|
||||
"GHCi lacks `:complete' support (try installing GHC 7.8+ or ghci-ng)")
|
||||
(when rawstr
|
||||
;; parse REPL response if any
|
||||
(let* ((s1 (split-string rawstr "\r?\n" t))
|
||||
(cs (mapcar #'haskell-string-literal-decode (cdr s1)))
|
||||
(h0 (car s1))) ;; "<limit count> <all count> <unused string>"
|
||||
(unless (string-match
|
||||
"\\`\\([0-9]+\\) \\([0-9]+\\) \\(\".*\"\\)\\'"
|
||||
h0)
|
||||
(error "Invalid `:complete' response"))
|
||||
(let ((cnt1 (match-string 1 h0))
|
||||
(h1 (haskell-string-literal-decode (match-string 3 h0))))
|
||||
(unless (= (string-to-number cnt1) (length cs))
|
||||
(error "Lengths inconsistent in `:complete' reponse"))
|
||||
(cons h1 cs)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Accessing the process
|
||||
|
||||
(defun haskell-process-get (process key)
|
||||
"Get the PROCESS's KEY value.
|
||||
Returns nil if KEY not set."
|
||||
(cdr (assq key process)))
|
||||
|
||||
(defun haskell-process-set (process key value)
|
||||
"Set the PROCESS's KEY to VALUE.
|
||||
Returns newly set VALUE."
|
||||
(if process
|
||||
(let ((cell (assq key process)))
|
||||
(if cell
|
||||
(setcdr cell value) ; modify cell in-place
|
||||
(setcdr process (cons (cons key value) (cdr process))) ; new cell
|
||||
value))
|
||||
(display-warning 'haskell-interactive
|
||||
"`haskell-process-set' called with nil process")))
|
||||
|
||||
;; Wrappers using haskell-process-{get,set}
|
||||
|
||||
(defun haskell-process-set-sent-stdin (p v)
|
||||
"We've sent stdin, so let's not clear the output at the end."
|
||||
(haskell-process-set p 'sent-stdin v))
|
||||
|
||||
(defun haskell-process-sent-stdin-p (p)
|
||||
"Did we send any stdin to the process during evaluation?"
|
||||
(haskell-process-get p 'sent-stdin))
|
||||
|
||||
(defun haskell-process-set-suggested-imports (p v)
|
||||
"Remember what imports have been suggested, to avoid
|
||||
re-asking about the same imports."
|
||||
(haskell-process-set p 'suggested-imported v))
|
||||
|
||||
(defun haskell-process-suggested-imports (p)
|
||||
"Get what modules have already been suggested and accepted."
|
||||
(haskell-process-get p 'suggested-imported))
|
||||
|
||||
(defun haskell-process-set-evaluating (p v)
|
||||
"Set status of evaluating to be on/off."
|
||||
(haskell-process-set p 'evaluating v))
|
||||
|
||||
(defun haskell-process-evaluating-p (p)
|
||||
"Get status of evaluating (on/off)."
|
||||
(haskell-process-get p 'evaluating))
|
||||
|
||||
(defun haskell-process-set-process (p v)
|
||||
"Set the process's inferior process."
|
||||
(haskell-process-set p 'inferior-process v))
|
||||
|
||||
(defun haskell-process-process (p)
|
||||
"Get the process child."
|
||||
(haskell-process-get p 'inferior-process))
|
||||
|
||||
(defun haskell-process-name (p)
|
||||
"Get the process name."
|
||||
(haskell-process-get p 'name))
|
||||
|
||||
(defun haskell-process-cmd (p)
|
||||
"Get the process's current command.
|
||||
Return nil if no current command."
|
||||
(haskell-process-get p 'current-command))
|
||||
|
||||
(defun haskell-process-set-cmd (p v)
|
||||
"Set the process's current command."
|
||||
(haskell-process-set-evaluating p nil)
|
||||
(haskell-process-set-sent-stdin p nil)
|
||||
(haskell-process-set-suggested-imports p nil)
|
||||
(haskell-process-set p 'current-command v))
|
||||
|
||||
(defun haskell-process-response (p)
|
||||
"Get the process's current response."
|
||||
(haskell-process-get p 'current-response))
|
||||
|
||||
(defun haskell-process-session (p)
|
||||
"Get the process's current session."
|
||||
(haskell-process-get p 'session))
|
||||
|
||||
(defun haskell-process-set-response (p v)
|
||||
"Set the process's current response."
|
||||
(haskell-process-set p 'current-response v))
|
||||
|
||||
(defun haskell-process-set-session (p v)
|
||||
"Set the process's current session."
|
||||
(haskell-process-set p 'session v))
|
||||
|
||||
(defun haskell-process-response-cursor (p)
|
||||
"Get the process's current response cursor."
|
||||
(haskell-process-get p 'current-response-cursor))
|
||||
|
||||
(defun haskell-process-set-response-cursor (p v)
|
||||
"Set the process's response cursor."
|
||||
(haskell-process-set p 'current-response-cursor v))
|
||||
|
||||
;; low-level command queue operations
|
||||
|
||||
(defun haskell-process-restarting (process)
|
||||
"Is the PROCESS restarting?"
|
||||
(haskell-process-get process 'is-restarting))
|
||||
|
||||
(defun haskell-process-cmd-queue (process)
|
||||
"Get the PROCESS' command queue.
|
||||
New entries get added to the end of the list. Use
|
||||
`haskell-process-cmd-queue-add' and
|
||||
`haskell-process-cmd-queue-pop' to modify the command queue."
|
||||
(haskell-process-get process 'command-queue))
|
||||
|
||||
(defun haskell-process-cmd-queue-add (process cmd)
|
||||
"Add CMD to end of PROCESS's command queue."
|
||||
(cl-check-type cmd haskell-command)
|
||||
(haskell-process-set process
|
||||
'command-queue
|
||||
(append (haskell-process-cmd-queue process)
|
||||
(list cmd))))
|
||||
|
||||
(defun haskell-process-cmd-queue-pop (process)
|
||||
"Pop the PROCESS' next entry from command queue.
|
||||
Returns nil if queue is empty."
|
||||
(let ((queue (haskell-process-cmd-queue process)))
|
||||
(when queue
|
||||
(haskell-process-set process 'command-queue (cdr queue))
|
||||
(car queue))))
|
||||
|
||||
|
||||
(defun haskell-process-unignore-file (session file)
|
||||
"
|
||||
|
||||
Note to Windows Emacs hackers:
|
||||
|
||||
chmod is how to change the mode of files in POSIX
|
||||
systems. This will not work on your operating
|
||||
system.
|
||||
|
||||
There is a command a bit like chmod called \"Calcs\"
|
||||
that you can try using here:
|
||||
|
||||
http://technet.microsoft.com/en-us/library/bb490872.aspx
|
||||
|
||||
If it works, you can submit a patch to this
|
||||
function and remove this comment.
|
||||
"
|
||||
(shell-command (read-from-minibuffer "Permissions command: "
|
||||
(concat "chmod 700 "
|
||||
file)))
|
||||
(haskell-session-modify
|
||||
session
|
||||
'ignored-files
|
||||
(lambda (files)
|
||||
(cl-remove-if (lambda (path)
|
||||
(string= path file))
|
||||
files))))
|
||||
|
||||
(defun haskell-command-exec-go (command)
|
||||
"Call the command's go function."
|
||||
(let ((go-func (haskell-command-go command)))
|
||||
(when go-func
|
||||
(funcall go-func (haskell-command-state command)))))
|
||||
|
||||
(defun haskell-command-exec-complete (command response)
|
||||
"Call the command's complete function."
|
||||
(let ((comp-func (haskell-command-complete command)))
|
||||
(when comp-func
|
||||
(condition-case-unless-debug e
|
||||
(funcall comp-func
|
||||
(haskell-command-state command)
|
||||
response)
|
||||
(quit (message "Quit"))
|
||||
(error (message "Haskell process command errored with: %S" e))))))
|
||||
|
||||
(defun haskell-command-exec-live (command response)
|
||||
"Trigger the command's live updates callback."
|
||||
(let ((live-func (haskell-command-live command)))
|
||||
(when live-func
|
||||
(funcall live-func
|
||||
(haskell-command-state command)
|
||||
response))))
|
||||
|
||||
(provide 'haskell-process)
|
||||
|
||||
;;; haskell-process.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-process.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-process.elc
Normal file
Binary file not shown.
124
elpa/haskell-mode-20171022.26/haskell-repl.el
Normal file
124
elpa/haskell-mode-20171022.26/haskell-repl.el
Normal file
@@ -0,0 +1,124 @@
|
||||
;;; haskell-repl.el --- REPL evaluation -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-interactive-mode)
|
||||
(require 'haskell-collapse)
|
||||
|
||||
(defun haskell-interactive-handle-expr ()
|
||||
"Handle an inputted expression at the REPL."
|
||||
(let ((expr (haskell-interactive-mode-input)))
|
||||
(if (string= "" (replace-regexp-in-string " " "" expr))
|
||||
;; Just make a new prompt on space-only input
|
||||
(progn
|
||||
(goto-char (point-max))
|
||||
(insert "\n")
|
||||
(haskell-interactive-mode-prompt))
|
||||
(when (haskell-interactive-at-prompt)
|
||||
(cond
|
||||
;; If already evaluating, then the user is trying to send
|
||||
;; input to the REPL during evaluation. Most likely in
|
||||
;; response to a getLine-like function.
|
||||
((and (haskell-process-evaluating-p (haskell-interactive-process))
|
||||
(= (line-end-position) (point-max)))
|
||||
(goto-char (point-max))
|
||||
(let ((process (haskell-interactive-process))
|
||||
(string (buffer-substring-no-properties
|
||||
haskell-interactive-mode-result-end
|
||||
(point))))
|
||||
;; here we need to go to end of line again as evil-mode
|
||||
;; might have managed to put us one char back
|
||||
(goto-char (point-max))
|
||||
(insert "\n")
|
||||
;; Bring the marker forward
|
||||
(setq haskell-interactive-mode-result-end
|
||||
(point-max))
|
||||
(haskell-process-set-sent-stdin process t)
|
||||
(haskell-process-send-string process string)))
|
||||
;; Otherwise we start a normal evaluation call.
|
||||
(t (setq haskell-interactive-mode-old-prompt-start
|
||||
(copy-marker haskell-interactive-mode-prompt-start))
|
||||
(set-marker haskell-interactive-mode-prompt-start (point-max))
|
||||
(haskell-interactive-mode-history-add expr)
|
||||
(haskell-interactive-mode-do-expr expr)))))))
|
||||
|
||||
(defun haskell-interactive-mode-do-expr (expr)
|
||||
(cond
|
||||
((string-match "^:present " expr)
|
||||
(haskell-interactive-mode-do-presentation (replace-regexp-in-string "^:present " "" expr)))
|
||||
(t
|
||||
(haskell-interactive-mode-run-expr expr))))
|
||||
|
||||
(defun haskell-interactive-mode-run-expr (expr)
|
||||
"Run the given expression."
|
||||
(let ((session (haskell-interactive-session))
|
||||
(process (haskell-interactive-process)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list session process expr 0)
|
||||
:go (lambda (state)
|
||||
(goto-char (point-max))
|
||||
(insert "\n")
|
||||
(setq haskell-interactive-mode-result-end
|
||||
(point-max))
|
||||
(haskell-process-send-string (cadr state)
|
||||
(haskell-interactive-mode-multi-line (cl-caddr state)))
|
||||
(haskell-process-set-evaluating (cadr state) t))
|
||||
:live (lambda (state buffer)
|
||||
(unless (and (string-prefix-p ":q" (cl-caddr state))
|
||||
(string-prefix-p (cl-caddr state) ":quit"))
|
||||
(let* ((cursor (cl-cadddr state))
|
||||
(next (replace-regexp-in-string
|
||||
haskell-process-prompt-regex
|
||||
""
|
||||
(substring buffer cursor))))
|
||||
(haskell-interactive-mode-eval-result (car state) next)
|
||||
(setf (cl-cdddr state) (list (length buffer)))
|
||||
nil)))
|
||||
:complete
|
||||
(lambda (state response)
|
||||
(haskell-process-set-evaluating (cadr state) nil)
|
||||
(unless (haskell-interactive-mode-trigger-compile-error state response)
|
||||
(haskell-interactive-mode-expr-result state response)))))))
|
||||
|
||||
(defun haskell-interactive-mode-expr-result (state response)
|
||||
"Print the result of evaluating the expression."
|
||||
(let ((response
|
||||
(with-temp-buffer
|
||||
(insert response)
|
||||
(haskell-interactive-mode-handle-h)
|
||||
(buffer-string))))
|
||||
(when haskell-interactive-mode-eval-mode
|
||||
(unless (haskell-process-sent-stdin-p (cadr state))
|
||||
(haskell-interactive-mode-eval-as-mode (car state) response))))
|
||||
(haskell-interactive-mode-prompt (car state)))
|
||||
|
||||
(defun haskell-interactive-mode-eval-as-mode (session text)
|
||||
"Insert TEXT font-locked according to `haskell-interactive-mode-eval-mode'."
|
||||
(with-current-buffer (haskell-session-interactive-buffer session)
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region (1+ haskell-interactive-mode-prompt-start) (point))
|
||||
(goto-char (point-max))
|
||||
(insert (haskell-fontify-as-mode text
|
||||
haskell-interactive-mode-eval-mode))
|
||||
(when haskell-interactive-mode-collapse
|
||||
(haskell-hide-toggle)))))
|
||||
|
||||
(provide 'haskell-repl)
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-repl.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-repl.elc
Normal file
Binary file not shown.
41
elpa/haskell-mode-20171022.26/haskell-sandbox.el
Normal file
41
elpa/haskell-mode-20171022.26/haskell-sandbox.el
Normal file
@@ -0,0 +1,41 @@
|
||||
;;; haskell-sandbox.el --- Support for sandboxes -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-session)
|
||||
|
||||
(defun haskell-sandbox-path (session)
|
||||
"If there is a haskell-session, return the path to the usual sandbox location."
|
||||
(concat (haskell-session-cabal-dir session)
|
||||
"/.cabal-sandbox"))
|
||||
|
||||
(defun haskell-sandbox-exists-p (session)
|
||||
"Is there a cabal sandbox?"
|
||||
(file-exists-p (haskell-sandbox-path session)))
|
||||
|
||||
(defun haskell-sandbox-pkgdb (session)
|
||||
"Get the package database of the sandbox."
|
||||
(let* ((files (directory-files (haskell-sandbox-path session)))
|
||||
(dir (car (cl-remove-if-not (lambda (file)
|
||||
(string-match ".conf.d$" file))
|
||||
files))))
|
||||
(when dir
|
||||
(concat (haskell-sandbox-path session) "/" dir))))
|
||||
|
||||
(provide 'haskell-sandbox)
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-sandbox.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-sandbox.elc
Normal file
Binary file not shown.
227
elpa/haskell-mode-20171022.26/haskell-session.el
Normal file
227
elpa/haskell-mode-20171022.26/haskell-session.el
Normal file
@@ -0,0 +1,227 @@
|
||||
;;; haskell-session.el --- Haskell sessions -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2011-2012 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Todo:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-cabal)
|
||||
(require 'haskell-customize)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Globals
|
||||
|
||||
;; Used internally
|
||||
(defvar-local haskell-session nil)
|
||||
|
||||
(defvar haskell-sessions (list)
|
||||
"All Haskell sessions in the Emacs session.")
|
||||
|
||||
(defun haskell-session-tags-filename (session)
|
||||
"Get the filename for the TAGS file."
|
||||
(concat (haskell-session-cabal-dir session) "/TAGS"))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Finding/clearing the session
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-maybe ()
|
||||
"Maybe get the Haskell session, return nil if there isn't one."
|
||||
(if (default-boundp 'haskell-session)
|
||||
haskell-session
|
||||
(setq haskell-session nil)))
|
||||
|
||||
(defun haskell-session-from-buffer ()
|
||||
"Get the session based on the buffer."
|
||||
(when (and (buffer-file-name)
|
||||
(consp haskell-sessions))
|
||||
(cl-reduce (lambda (acc a)
|
||||
(let ((dir (haskell-session-get a 'cabal-dir)))
|
||||
(if dir
|
||||
(if (string-prefix-p dir
|
||||
(file-name-directory (buffer-file-name)))
|
||||
(if acc
|
||||
(if (and
|
||||
(> (length (haskell-session-get a 'cabal-dir))
|
||||
(length (haskell-session-get acc 'cabal-dir))))
|
||||
a
|
||||
acc)
|
||||
a)
|
||||
acc)
|
||||
acc)))
|
||||
haskell-sessions
|
||||
:initial-value nil)))
|
||||
|
||||
(defun haskell-session-default-name ()
|
||||
"Generate a default project name for the new project prompt."
|
||||
(let ((file (haskell-cabal-find-file)))
|
||||
(or (when file
|
||||
(downcase (file-name-sans-extension
|
||||
(file-name-nondirectory file))))
|
||||
"haskell")))
|
||||
|
||||
(defun haskell-session-assign (session)
|
||||
"Assing current buffer to SESSION.
|
||||
|
||||
This could be helpful for temporary or auxiliary buffers such as
|
||||
presentation mode buffers (e.g. in case when session is killed
|
||||
with all relevant buffers)."
|
||||
(setq-local haskell-session session))
|
||||
|
||||
(defun haskell-session-choose ()
|
||||
"Find a session by choosing from a list of the current sessions."
|
||||
(when haskell-sessions
|
||||
(let* ((session-name (funcall haskell-completing-read-function
|
||||
"Choose Haskell session: "
|
||||
(cl-remove-if (lambda (name)
|
||||
(and haskell-session
|
||||
(string= (haskell-session-name haskell-session)
|
||||
name)))
|
||||
(mapcar 'haskell-session-name haskell-sessions))))
|
||||
(session (cl-find-if (lambda (session)
|
||||
(string= (haskell-session-name session)
|
||||
session-name))
|
||||
haskell-sessions)))
|
||||
session)))
|
||||
|
||||
(defun haskell-session-clear ()
|
||||
"Clear the buffer of any Haskell session choice."
|
||||
(setq-local haskell-session nil))
|
||||
|
||||
(defun haskell-session-lookup (name)
|
||||
"Get the session by name."
|
||||
(cl-remove-if-not (lambda (s)
|
||||
(string= name (haskell-session-name s)))
|
||||
haskell-sessions))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Session modules
|
||||
|
||||
(defun haskell-session-strip-dir (session file)
|
||||
"Strip the load dir from the file path."
|
||||
(let ((cur-dir (haskell-session-current-dir session)))
|
||||
(if (> (length file) (length cur-dir))
|
||||
(if (string= (substring file 0 (length cur-dir))
|
||||
cur-dir)
|
||||
(replace-regexp-in-string
|
||||
"^[/\\]" ""
|
||||
(substring file
|
||||
(length cur-dir)))
|
||||
file)
|
||||
file)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Accessing the session
|
||||
|
||||
(defun haskell-session-current-dir (s)
|
||||
"Get the session current directory."
|
||||
(let ((dir (haskell-session-get s 'current-dir)))
|
||||
(or dir
|
||||
(error "No current directory."))))
|
||||
|
||||
(defun haskell-session-name (s)
|
||||
"Get the session name."
|
||||
(haskell-session-get s 'name))
|
||||
|
||||
(defun haskell-session-target (s)
|
||||
"Get the session build target.
|
||||
If `haskell-process-load-or-reload-prompt' is nil, accept `default'."
|
||||
(let* ((maybe-target (haskell-session-get s 'target))
|
||||
(target (if maybe-target maybe-target
|
||||
(let ((new-target
|
||||
(if haskell-process-load-or-reload-prompt
|
||||
(read-string "build target (empty for default):")
|
||||
"")))
|
||||
(haskell-session-set-target s new-target)))))
|
||||
(if (not (string= target "")) target nil)))
|
||||
|
||||
(defun haskell-session-set-target (s target)
|
||||
"Set the session build target."
|
||||
(haskell-session-set s 'target target))
|
||||
|
||||
(defun haskell-session-set-interactive-buffer (s v)
|
||||
"Set the session interactive buffer."
|
||||
(haskell-session-set s 'interactive-buffer v))
|
||||
|
||||
(defun haskell-session-set-process (s v)
|
||||
"Set the session process."
|
||||
(haskell-session-set s 'process v))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-process (s)
|
||||
"Get the session process."
|
||||
(haskell-session-get s 'process))
|
||||
|
||||
(defun haskell-session-set-cabal-dir (s v)
|
||||
"Set the session cabal-dir."
|
||||
(let ((true-path (file-truename v)))
|
||||
(haskell-session-set s 'cabal-dir true-path)
|
||||
(haskell-session-set-cabal-checksum s true-path)))
|
||||
|
||||
(defun haskell-session-set-current-dir (s v)
|
||||
"Set the session current directory."
|
||||
(let ((true-path (file-truename v)))
|
||||
(haskell-session-set s 'current-dir true-path)))
|
||||
|
||||
(defun haskell-session-set-cabal-checksum (s cabal-dir)
|
||||
"Set the session checksum of .cabal files"
|
||||
(haskell-session-set s 'cabal-checksum
|
||||
(haskell-cabal-compute-checksum cabal-dir)))
|
||||
|
||||
(defun haskell-session-cabal-dir (s)
|
||||
"Get the session cabal-dir."
|
||||
(or (haskell-session-get s 'cabal-dir)
|
||||
(let ((set-dir (haskell-cabal-get-dir (not haskell-process-load-or-reload-prompt))))
|
||||
(if set-dir
|
||||
(progn (haskell-session-set-cabal-dir s set-dir)
|
||||
set-dir)
|
||||
(haskell-session-cabal-dir s)))))
|
||||
|
||||
(defun haskell-session-modify (session key update)
|
||||
"Update the value at KEY in SESSION with UPDATE."
|
||||
(haskell-session-set
|
||||
session
|
||||
key
|
||||
(funcall update
|
||||
(haskell-session-get session key))))
|
||||
|
||||
(defun haskell-session-get (session key)
|
||||
"Get the SESSION's KEY value.
|
||||
Returns nil if KEY not set."
|
||||
(cdr (assq key session)))
|
||||
|
||||
(defun haskell-session-set (session key value)
|
||||
"Set the SESSION's KEY to VALUE.
|
||||
Returns newly set VALUE."
|
||||
(let ((cell (assq key session)))
|
||||
(if cell
|
||||
(setcdr cell value) ; modify cell in-place
|
||||
(setcdr session (cons (cons key value) (cdr session))) ; new cell
|
||||
value)))
|
||||
|
||||
(provide 'haskell-session)
|
||||
|
||||
;;; haskell-session.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-session.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-session.elc
Normal file
Binary file not shown.
129
elpa/haskell-mode-20171022.26/haskell-sort-imports.el
Normal file
129
elpa/haskell-mode-20171022.26/haskell-sort-imports.el
Normal file
@@ -0,0 +1,129 @@
|
||||
;;; haskell-sort-imports.el --- Sort the list of Haskell imports at the point alphabetically -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2010 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This program is free software: you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation, either version 3 of
|
||||
;; the License, or (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be
|
||||
;; useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
;; PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public
|
||||
;; License along with this program. If not, see
|
||||
;; <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; If the region is active it sorts the imports within the
|
||||
;; region.
|
||||
|
||||
;; This will align and sort the columns of the current import
|
||||
;; list. It's more or less the coolest thing on the planet.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar haskell-sort-imports-regexp
|
||||
(concat "^import[ ]+"
|
||||
"\\(qualified \\)?"
|
||||
"[ ]*\\(\"[^\"]*\" \\)?"
|
||||
"[ ]*\\([A-Za-z0-9_.']*.*\\)"))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-sort-imports ()
|
||||
"Sort the import list at point. It sorts the current group
|
||||
i.e. an import list separated by blank lines on either side.
|
||||
|
||||
If the region is active, it will restrict the imports to sort
|
||||
within that region."
|
||||
(interactive)
|
||||
(when (haskell-sort-imports-at-import)
|
||||
(let* ((points (haskell-sort-imports-decl-points))
|
||||
(current-string (buffer-substring-no-properties (car points)
|
||||
(cdr points)))
|
||||
(current-offset (- (point) (car points))))
|
||||
(if (region-active-p)
|
||||
(progn (goto-char (region-beginning))
|
||||
(haskell-sort-imports-goto-import-start))
|
||||
(haskell-sort-imports-goto-group-start))
|
||||
(let* ((start (point))
|
||||
(imports (haskell-sort-imports-collect-imports))
|
||||
(sorted (sort (cl-copy-list imports)
|
||||
(lambda (a b)
|
||||
(string< (haskell-sort-imports-normalize a)
|
||||
(haskell-sort-imports-normalize b))))))
|
||||
(when (not (equal imports sorted))
|
||||
(delete-region start (point))
|
||||
(mapc (lambda (import) (insert import "\n")) sorted))
|
||||
(goto-char start)
|
||||
(when (search-forward current-string nil t 1)
|
||||
(forward-char (- (length current-string)))
|
||||
(forward-char current-offset))))))
|
||||
|
||||
(defun haskell-sort-imports-normalize (i)
|
||||
"Normalize an import, if possible, so that it can be sorted."
|
||||
(if (string-match haskell-sort-imports-regexp
|
||||
i)
|
||||
(match-string 3 i)
|
||||
i))
|
||||
|
||||
(defun haskell-sort-imports-collect-imports ()
|
||||
(let ((imports (list)))
|
||||
(while (looking-at "import")
|
||||
(let* ((points (haskell-sort-imports-decl-points))
|
||||
(string (buffer-substring-no-properties (car points)
|
||||
(cdr points))))
|
||||
(goto-char (min (1+ (cdr points))
|
||||
(point-max)))
|
||||
(setq imports (cons string imports))))
|
||||
(reverse (delq nil (delete-dups imports)))))
|
||||
|
||||
(defun haskell-sort-imports-goto-group-start ()
|
||||
"Go to the start of the import group."
|
||||
(or (and (search-backward "\n\n" nil t 1)
|
||||
(goto-char (+ 2 (line-end-position))))
|
||||
(when (search-backward-regexp "^module " nil t 1)
|
||||
(goto-char (1+ (line-end-position))))
|
||||
(goto-char (point-min))))
|
||||
|
||||
(defun haskell-sort-imports-at-import ()
|
||||
"Are we at an import?"
|
||||
(save-excursion
|
||||
(haskell-sort-imports-goto-import-start)
|
||||
(looking-at "import")))
|
||||
|
||||
(defun haskell-sort-imports-goto-import-start ()
|
||||
"Go to the start of the import."
|
||||
(goto-char (car (haskell-sort-imports-decl-points))))
|
||||
|
||||
(defun haskell-sort-imports-decl-points ()
|
||||
"Get the points of the declaration."
|
||||
(save-excursion
|
||||
(let ((start (or (progn (goto-char (line-end-position))
|
||||
(search-backward-regexp "^[^ \n]" nil t 1)
|
||||
(unless (or (looking-at "^-}$")
|
||||
(looking-at "^{-$"))
|
||||
(point)))
|
||||
0))
|
||||
(end (progn (goto-char (1+ (point)))
|
||||
(or (when (search-forward-regexp "[\n]+[^ \n]" nil t 1)
|
||||
(forward-char -1)
|
||||
(search-backward-regexp "[^\n ]" nil t)
|
||||
(line-end-position))
|
||||
(when (search-forward-regexp "\n" nil t 1)
|
||||
(1- (point)))
|
||||
(point-max)))))
|
||||
(cons start end))))
|
||||
|
||||
(provide 'haskell-sort-imports)
|
||||
|
||||
;;; haskell-sort-imports.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-sort-imports.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-sort-imports.elc
Normal file
Binary file not shown.
219
elpa/haskell-mode-20171022.26/haskell-string.el
Normal file
219
elpa/haskell-mode-20171022.26/haskell-string.el
Normal file
@@ -0,0 +1,219 @@
|
||||
;;; haskell-string.el --- Haskell related string utilities -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2013 Herbert Valerio Riedel
|
||||
|
||||
;; Author: Herbert Valerio Riedel <hvr@gnu.org>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Todo:
|
||||
|
||||
;; - write ERT tests
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(defun haskell-string-trim (string)
|
||||
"Remove whitespace around STRING.
|
||||
|
||||
A Whitespace character is defined in the Haskell Report as follows
|
||||
|
||||
whitechar -> newline | vertab | space | tab | uniWhite
|
||||
newline -> return linefeed | return | linefeed | formfeed
|
||||
uniWhite -> any Unicode character defined as whitespace
|
||||
|
||||
Note: The implementation currently only supports ASCII
|
||||
white-space characters, i.e. the implemention doesn't
|
||||
consider uniWhite."
|
||||
|
||||
(let ((s1 (if (string-match "[\t\n\v\f\r ]+\\'" string) (replace-match "" t t string) string)))
|
||||
(if (string-match "\\`[\t\n\v\f\r ]+" s1) (replace-match "" t t s1) s1)))
|
||||
|
||||
(defun haskell-string-only-spaces-p (string)
|
||||
"Return t if STRING contains only whitespace (or is empty)."
|
||||
(string= "" (haskell-string-trim string)))
|
||||
|
||||
(defun haskell-string-take (string n)
|
||||
"Return (up to) N character length prefix of STRING."
|
||||
(substring string 0 (min (length string) n)))
|
||||
|
||||
(defconst haskell-string-literal-encode-ascii-array
|
||||
[ "\\NUL" "\\SOH" "\\STX" "\\ETX" "\\EOT" "\\ENQ" "\\ACK" "\\a" "\\b" "\\t" "\\n" "\\v" "\\f" "\\r" "\\SO" "\\SI" "\\DLE" "\\DC1" "\\DC2" "\\DC3" "\\DC4" "\\NAK" "\\SYN" "\\ETB" "\\CAN" "\\EM" "\\SUB" "\\ESC" "\\FS" "\\GS" "\\RS" "\\US" " " "!" "\\\"" "#" "$" "%" "&" "'" "(" ")" "*" "+" "," "-" "." "/" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" ":" ";" "<" "=" ">" "?" "@" "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "[" "\\\\" "]" "^" "_" "`" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "{" "|" "}" "~" "\\DEL" ]
|
||||
"Array of encodings for 7-bit ASCII character points indexed by ASCII value.")
|
||||
|
||||
(defun haskell-string-literal-encode (str &optional no-quotes)
|
||||
"Encode STR according Haskell escape rules using 7-bit ASCII representation.
|
||||
|
||||
The serialization has been implemented to closely match the
|
||||
behaviour of GHC's Show instance for Strings.
|
||||
|
||||
If NO-QUOTES is non-nil, omit wrapping result in quotes.
|
||||
|
||||
This is the dual operation to `haskell-string-literal-decode'."
|
||||
|
||||
(let ((lastc -1))
|
||||
(let ((encode (lambda (c)
|
||||
(let ((lc lastc))
|
||||
(setq lastc c)
|
||||
(if (>= c 128) ;; if non-ASCII code point
|
||||
(format "\\%d" c)
|
||||
;; else, for ASCII code points
|
||||
(if (or (and (= lc 14) (= c ?H)) ;; "\SO\&H"
|
||||
(and (>= lc 128) (>= c ?0) (<= c ?9))) ;; "\123\&4"
|
||||
(concat "\\&" (aref haskell-string-literal-encode-ascii-array c))
|
||||
(aref haskell-string-literal-encode-ascii-array c)
|
||||
))))))
|
||||
|
||||
(if no-quotes
|
||||
(mapconcat encode str "")
|
||||
(concat "\"" (mapconcat encode str "") "\"")))))
|
||||
|
||||
(defconst haskell-string-literal-escapes-regexp
|
||||
(concat "[\\]\\(?:"
|
||||
(regexp-opt (append
|
||||
(mapcar (lambda (c) (format "%c" c))
|
||||
"abfnrtv\\\"'&") ;; "charesc" escape sequences
|
||||
(mapcar (lambda (c) (format "^%c" c))
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ@[\\]^_") ;; "cntrl" escape sequences
|
||||
(mapcar (lambda (s) (format "%s" s))
|
||||
(split-string "NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR
|
||||
SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC
|
||||
FS GS RS US SP DEL")))) ;; "ascii" (w\o "cntrl") escape sequences
|
||||
"\\|" "[\t\n\v\f\r ]+[\\]" ;; whitespace gaps
|
||||
"\\|" "[0-9]+" ;; decimal escape sequence
|
||||
"\\|" "o[0-7]+" ;; octal escape sequence
|
||||
"\\|" "x[0-9a-f]+" ;; hex escape sequence
|
||||
"\\)?") ;; everything else is an invalid escape sequence
|
||||
"Regexp for matching escape codes in string literals.
|
||||
See Haskell Report Sect 2.6,
|
||||
URL `http://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6',
|
||||
for more details.")
|
||||
|
||||
(defconst haskell-string-literal-decode1-table
|
||||
(let ((h (make-hash-table :test 'equal)))
|
||||
(mapc (lambda (c) (puthash (concat "\\" (car c)) (cdr c) h))
|
||||
'(;; ascii-escapes
|
||||
("NUL" . "\x00") ("SOH" . "\x01") ("STX" . "\x02") ("ETX" . "\x03") ("EOT" . "\x04") ("ENQ" . "\x05")
|
||||
("ACK" . "\x06") ("BEL" . "\x07") ("BS" . "\x08") ("HT" . "\x09") ("LF" . "\x0a") ("VT" . "\x0b")
|
||||
("FF" . "\x0c") ("CR" . "\x0d") ("SO" . "\x0e") ("SI" . "\x0f") ("DLE" . "\x10") ("DC1" . "\x11")
|
||||
("DC2" . "\x12") ("DC3" . "\x13") ("DC4" . "\x14") ("NAK" . "\x15") ("SYN" . "\x16") ("ETB" . "\x17")
|
||||
("CAN" . "\x18") ("EM" . "\x19") ("SUB" . "\x1a") ("ESC" . "\x1b") ("FS" . "\x1c") ("GS" . "\x1d")
|
||||
("RS" . "\x1e") ("US" . "\x1f") ("SP" . "\x20") ("DEL" . "\x7f" )
|
||||
;; C-compatible single-char escape sequences
|
||||
("a" . "\x07") ("b" . "\x08") ("f" . "\x0c") ("n" . "\x0a") ("r" . "\x0d") ("t" . "\x09") ("v" . "\x0b")
|
||||
;; trivial escapes
|
||||
("\\" . "\\") ("\"" . "\"") ("'" . "'")
|
||||
;; "empty" escape
|
||||
("&" . "")))
|
||||
h)
|
||||
"Hash table containing irregular escape sequences and their decoded strings.
|
||||
Used by `haskell-string-literal-decode1'.")
|
||||
|
||||
(defun haskell-string-literal-decode1 (l)
|
||||
"Decode a single string literal escape sequence.
|
||||
L must contain exactly one escape sequence.
|
||||
This is an internal function used by `haskell-string-literal-decode'."
|
||||
(let ((case-fold-search nil))
|
||||
(cond
|
||||
((gethash l haskell-string-literal-decode1-table))
|
||||
((string-match "\\`[\\][0-9]+\\'" l) (char-to-string (string-to-number (substring l 1) 10)))
|
||||
((string-match "\\`[\\]x[[:xdigit:]]+\\'" l) (char-to-string (string-to-number (substring l 2) 16)))
|
||||
((string-match "\\`[\\]o[0-7]+\\'" l) (char-to-string (string-to-number (substring l 2) 8)))
|
||||
((string-match "\\`[\\]\\^[@-_]\\'" l) (char-to-string (- (aref l 2) ?@))) ;; "cntrl" escapes
|
||||
((string-match "\\`[\\][\t\n\v\f\r ]+[\\]\\'" l) "") ;; whitespace gap
|
||||
(t (error "Invalid escape sequence")))))
|
||||
|
||||
(defun haskell-string-literal-decode (estr &optional no-quotes)
|
||||
"Decode a Haskell string-literal.
|
||||
If NO-QUOTES is nil, ESTR must be surrounded by quotes.
|
||||
|
||||
This is the dual operation to `haskell-string-literal-encode'."
|
||||
(if (and (not no-quotes)
|
||||
(string-match-p "\\`\"[^\\\"[:cntrl:]]*\"\\'" estr))
|
||||
(substring estr 1 -1) ;; optimized fast-path for trivial strings
|
||||
(let ((s (if no-quotes ;; else: do general decoding
|
||||
estr
|
||||
(if (string-match-p "\\`\".*\"\\'" estr)
|
||||
(substring estr 1 -1)
|
||||
(error "String literal must be delimited by quotes"))))
|
||||
(case-fold-search nil))
|
||||
(replace-regexp-in-string haskell-string-literal-escapes-regexp #'haskell-string-literal-decode1 s t t))))
|
||||
|
||||
(defun haskell-string-ellipsize (string n)
|
||||
"Return STRING truncated to (at most) N characters.
|
||||
If truncation occured, last character in string is replaced by `…'.
|
||||
See also `haskell-string-take'."
|
||||
(cond
|
||||
((<= (length string) n) string) ;; no truncation needed
|
||||
((< n 1) "")
|
||||
(t (concat (substring string 0 (1- n)) "…"))))
|
||||
|
||||
(defun haskell-string-chomp (str)
|
||||
"Chomp leading and tailing whitespace from STR."
|
||||
(while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'"
|
||||
str)
|
||||
(setq str (replace-match "" t t str)))
|
||||
str)
|
||||
|
||||
(defun haskell-string-split-to-lines (str)
|
||||
"Split STR to lines and return a list of strings with preceeding and
|
||||
succeding space removed."
|
||||
(when (stringp str)
|
||||
(cl-mapcar #'haskell-string-chomp (split-string str "\n"))))
|
||||
|
||||
(defun haskell-string-trim-prefix (prefix str)
|
||||
"If PREFIX is prefix of STR, the string is trimmed."
|
||||
(when (and (stringp prefix)
|
||||
(stringp str))
|
||||
(if (string-prefix-p prefix str)
|
||||
(substring str (length prefix)))))
|
||||
|
||||
(defun haskell-string-trim-suffix (suffix str)
|
||||
"If SUFFIX is suffix of STR, the string is trimmed."
|
||||
(when (and (stringp suffix)
|
||||
(stringp str))
|
||||
(if (string-suffix-p suffix str)
|
||||
(substring str 0 (* -1 (length suffix))))))
|
||||
|
||||
(defun haskell-string-drop-qualifier (ident)
|
||||
"Drop qualifier from given identifier IDENT.
|
||||
|
||||
If the identifier is not qualified return it unchanged."
|
||||
(or (and (string-match "^\\([^.]*\\.\\)*\\(?1:[^.]+\\)$" ident)
|
||||
(match-string 1 ident))
|
||||
ident))
|
||||
|
||||
(defun haskell-mode-message-line (str)
|
||||
"Echo STR in mini-buffer.
|
||||
Given string is shrinken to single line, multiple lines just
|
||||
disturbs the programmer."
|
||||
(message "%s" (haskell-mode-one-line str (frame-width))))
|
||||
|
||||
(defun haskell-mode-one-line (str &optional width)
|
||||
"Try to fit STR as much as possible on one line according to given WIDTH."
|
||||
(unless width
|
||||
(setq width (length str)))
|
||||
(let* ((long-line (replace-regexp-in-string "\n" " " str))
|
||||
(condensed (replace-regexp-in-string
|
||||
" +" " " (haskell-string-trim long-line))))
|
||||
(truncate-string-to-width condensed width nil nil "…")))
|
||||
|
||||
(provide 'haskell-string)
|
||||
|
||||
;;; haskell-string.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-string.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-string.elc
Normal file
Binary file not shown.
300
elpa/haskell-mode-20171022.26/haskell-unicode-input-method.el
Normal file
300
elpa/haskell-mode-20171022.26/haskell-unicode-input-method.el
Normal file
@@ -0,0 +1,300 @@
|
||||
;;; haskell-unicode-input-method.el --- Haskell Unicode helper functions -*- coding: utf-8; lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2010-2011 Roel van Dijk
|
||||
|
||||
;; Author: Roel van Dijk <vandijk.roel@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'quail)
|
||||
|
||||
;;;###autoload
|
||||
(defun turn-on-haskell-unicode-input-method ()
|
||||
"Set input method `haskell-unicode'."
|
||||
(interactive)
|
||||
(set-input-method "haskell-unicode"))
|
||||
|
||||
(quail-define-package
|
||||
"haskell-unicode" ;; name
|
||||
"UTF-8" ;; language
|
||||
"\\" ;; title
|
||||
t ;; guidance
|
||||
"Haskell Unicode input method.
|
||||
Designed to be used with the Haskell UnicodeSyntax language
|
||||
extension in combination with the x-unicode-symbols set of
|
||||
packages (base-unicode-symbols and containers-unicode-symbols).
|
||||
" ;; docstring
|
||||
nil ;; translation-keys
|
||||
nil ;; forget-last-selection
|
||||
nil ;; deterministic
|
||||
nil ;; kbd-translate
|
||||
nil ;; show-layout
|
||||
nil ;; create-decode-map
|
||||
nil ;; maximum-shortest
|
||||
nil ;; overlay-plist
|
||||
nil ;; update-translation-function
|
||||
nil ;; conversion-keys
|
||||
t ;; simple
|
||||
)
|
||||
|
||||
(quail-define-rules
|
||||
;; Greek letters
|
||||
("alpha " ["α"])
|
||||
("Alpha " ["Α"])
|
||||
("beta " ["β"])
|
||||
("Beta " ["Β"])
|
||||
("gamma " ["γ"])
|
||||
("Gamma " ["Γ"])
|
||||
("delta " ["δ"])
|
||||
("Delta " ["Δ"])
|
||||
("epsilon " ["ε"])
|
||||
("Epsilon " ["Ε"])
|
||||
("zeta " ["ζ"])
|
||||
("Zeta " ["Ζ"])
|
||||
("eta " ["η"])
|
||||
("Eta " ["Η"])
|
||||
("theta " ["θ"])
|
||||
("Theta " ["Θ"])
|
||||
("iota " ["ι"])
|
||||
("Iota " ["Ι"])
|
||||
("kappa " ["κ"])
|
||||
("Kappa " ["Κ"])
|
||||
("lambda " ["λ"])
|
||||
("Lambda " ["Λ"])
|
||||
("lamda " ["λ"])
|
||||
("Lamda " ["Λ"])
|
||||
("mu " ["μ"])
|
||||
("Mu " ["Μ"])
|
||||
("nu " ["ν"])
|
||||
("Nu " ["Ν"])
|
||||
("xi " ["ξ"])
|
||||
("Xi " ["Ξ"])
|
||||
("omicron " ["ο"])
|
||||
("Omicron " ["Ο"])
|
||||
("pi " ["π"])
|
||||
("Pi " ["Π"])
|
||||
("rho " ["ρ"])
|
||||
("Rho " ["Ρ"])
|
||||
("sigma " ["σ"])
|
||||
("Sigma " ["Σ"])
|
||||
("tau " ["τ"])
|
||||
("Tau " ["Τ"])
|
||||
("upsilon " ["υ"])
|
||||
("Upsilon " ["Υ"])
|
||||
("phi " ["φ"])
|
||||
("Phi " ["Φ"])
|
||||
("chi " ["χ"])
|
||||
("Chi " ["Χ"])
|
||||
("psi " ["ψ"])
|
||||
("Psi " ["Ψ"])
|
||||
("omega " ["ω"])
|
||||
("Omega " ["Ω"])
|
||||
("digamma " ["ϝ"])
|
||||
("Digamma " ["Ϝ"])
|
||||
("san " ["ϻ"])
|
||||
("San " ["Ϻ"])
|
||||
("qoppa " ["ϙ"])
|
||||
("Qoppa " ["Ϙ"])
|
||||
("sampi " ["ϡ"])
|
||||
("Sampi " ["Ϡ"])
|
||||
("stigma " ["ϛ"])
|
||||
("Stigma " ["Ϛ"])
|
||||
("heta " ["ͱ"])
|
||||
("Heta " ["Ͱ"])
|
||||
("sho " ["ϸ"])
|
||||
("Sho " ["Ϸ"])
|
||||
|
||||
;; Double-struck letters
|
||||
("|A|" ["𝔸"])
|
||||
("|B|" ["𝔹"])
|
||||
("|C|" ["ℂ"])
|
||||
("|D|" ["𝔻"])
|
||||
("|E|" ["𝔼"])
|
||||
("|F|" ["𝔽"])
|
||||
("|G|" ["𝔾"])
|
||||
("|H|" ["ℍ"])
|
||||
("|I|" ["𝕀"])
|
||||
("|J|" ["𝕁"])
|
||||
("|K|" ["𝕂"])
|
||||
("|L|" ["𝕃"])
|
||||
("|M|" ["𝕄"])
|
||||
("|N|" ["ℕ"])
|
||||
("|O|" ["𝕆"])
|
||||
("|P|" ["ℙ"])
|
||||
("|Q|" ["ℚ"])
|
||||
("|R|" ["ℝ"])
|
||||
("|S|" ["𝕊"])
|
||||
("|T|" ["𝕋"])
|
||||
("|U|" ["𝕌"])
|
||||
("|V|" ["𝕍"])
|
||||
("|W|" ["𝕎"])
|
||||
("|X|" ["𝕏"])
|
||||
("|Y|" ["𝕐"])
|
||||
("|Z|" ["ℤ"])
|
||||
("|gamma|" ["ℽ"])
|
||||
("|Gamma|" ["ℾ"])
|
||||
("|pi|" ["ℼ"])
|
||||
("|Pi|" ["ℿ"])
|
||||
|
||||
;; Types
|
||||
("::" ["∷"])
|
||||
|
||||
;; Quantifiers
|
||||
("forall" ["∀"])
|
||||
("exists" ["∃"])
|
||||
|
||||
;; Arrows
|
||||
("->" ["→"])
|
||||
;; ("-->" ["⟶"])
|
||||
("<-" ["←"])
|
||||
;; ("<--" ["⟵"])
|
||||
;; ("<->" ["↔"])
|
||||
;; ("<-->" ["⟷"])
|
||||
|
||||
("=>" ["⇒"])
|
||||
;; ("==>" ["⟹"])
|
||||
;; ("<=" ["⇐"])
|
||||
;; ("<==" ["⟸"])
|
||||
;; ("<=>" ["⇔"])
|
||||
;; ("<==>" ["⟺"])
|
||||
|
||||
;; ("|->" ["↦"])
|
||||
;; ("|-->" ["⟼"])
|
||||
;; ("<-|" ["↤"])
|
||||
;; ("<--|" ["⟻"])
|
||||
|
||||
;; ("|=>" ["⤇"])
|
||||
;; ("|==>" ["⟾"])
|
||||
;; ("<=|" ["⤆"])
|
||||
;; ("<==|" ["⟽"])
|
||||
|
||||
("~>" ["⇝"])
|
||||
;; ("~~>" ["⟿"])
|
||||
("<~" ["⇜"])
|
||||
;; ("<~~" ["⬳"])
|
||||
|
||||
;; (">->" ["↣"])
|
||||
;; ("<-<" ["↢"])
|
||||
;; ("->>" ["↠"])
|
||||
;; ("<<-" ["↞"])
|
||||
|
||||
;; (">->>" ["⤖"])
|
||||
;; ("<<-<" ["⬻"])
|
||||
|
||||
;; ("<|-" ["⇽"])
|
||||
;; ("-|>" ["⇾"])
|
||||
;; ("<|-|>" ["⇿"])
|
||||
|
||||
;; ("<-/-" ["↚"])
|
||||
;; ("-/->" ["↛"])
|
||||
|
||||
;; ("<-|-" ["⇷"])
|
||||
;; ("-|->" ["⇸"])
|
||||
;; ("<-|->" ["⇹"])
|
||||
|
||||
;; ("<-||-" ["⇺"])
|
||||
;; ("-||->" ["⇻"])
|
||||
;; ("<-||->" ["⇼"])
|
||||
|
||||
;; ("-o->" ["⇴"])
|
||||
;; ("<-o-" ["⬰"])
|
||||
|
||||
;; Boolean operators
|
||||
;; ("not" ["¬"])
|
||||
("&&" ["∧"])
|
||||
("||" ["∨"])
|
||||
|
||||
;; Relational operators
|
||||
("==" ["≡"])
|
||||
("/=" ["≢" "≠"])
|
||||
("<=" ["≤"])
|
||||
(">=" ["≥"])
|
||||
("/<" ["≮"])
|
||||
("/>" ["≯"])
|
||||
|
||||
;; Arithmetic
|
||||
;; (" / " [" ÷ "])
|
||||
(" * " [" ⋅ "])
|
||||
|
||||
;; Containers / Collections
|
||||
;; ("++" ["⧺"])
|
||||
;; ("+++" ["⧻"])
|
||||
;; ("|||" ["⫴"])
|
||||
;; ("empty" ["∅"])
|
||||
("elem" ["∈"])
|
||||
("notElem" ["∉"])
|
||||
("member" ["∈"])
|
||||
("notMember" ["∉"])
|
||||
("union" ["∪"])
|
||||
("intersection" ["∩"])
|
||||
("isSubsetOf" ["⊆"])
|
||||
("isProperSubsetOf" ["⊂"])
|
||||
|
||||
;; Other
|
||||
;; ("<<" ["≪"])
|
||||
;; (">>" ["≫"])
|
||||
("<<<" ["⋘"])
|
||||
(">>>" ["⋙"])
|
||||
("<|" ["⊲"])
|
||||
("|>" ["⊳"])
|
||||
("><" ["⋈"])
|
||||
;; ("mempty" ["∅"])
|
||||
("mappend" ["⊕"])
|
||||
;; ("<*>" ["⊛"])
|
||||
(" . " [" ∘ "])
|
||||
("undefined" ["⊥"])
|
||||
(":=" ["≔"])
|
||||
("=:" ["≕"])
|
||||
("=def" ["≝"])
|
||||
("=?" ["≟"])
|
||||
("..." ["…"])
|
||||
|
||||
;; Braces
|
||||
;; ("[|" ["〚"])
|
||||
;; ("|]" ["〛"])
|
||||
|
||||
;; Numeric subscripts
|
||||
("_0 " ["₀"])
|
||||
("_1 " ["₁"])
|
||||
("_2 " ["₂"])
|
||||
("_3 " ["₃"])
|
||||
("_4 " ["₄"])
|
||||
("_5 " ["₅"])
|
||||
("_6 " ["₆"])
|
||||
("_7 " ["₇"])
|
||||
("_8 " ["₈"])
|
||||
("_9 " ["₉"])
|
||||
|
||||
;; Numeric superscripts
|
||||
("^0 " ["⁰"])
|
||||
("^1 " ["¹"])
|
||||
("^2 " ["²"])
|
||||
("^3 " ["³"])
|
||||
("^4 " ["⁴"])
|
||||
("^5 " ["⁵"])
|
||||
("^6 " ["⁶"])
|
||||
("^7 " ["⁷"])
|
||||
("^8 " ["⁸"])
|
||||
("^9 " ["⁹"])
|
||||
)
|
||||
|
||||
(provide 'haskell-unicode-input-method)
|
||||
|
||||
;;; haskell-unicode-input-method.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-unicode-input-method.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-unicode-input-method.elc
Normal file
Binary file not shown.
193
elpa/haskell-mode-20171022.26/haskell-utils.el
Normal file
193
elpa/haskell-mode-20171022.26/haskell-utils.el
Normal file
@@ -0,0 +1,193 @@
|
||||
;;; haskell-utils.el --- General utility functions used by haskell-mode modules -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright © 2013 Herbert Valerio Riedel
|
||||
;; 2016 Arthur Fayzrakhmanov
|
||||
|
||||
;; Author: Herbert Valerio Riedel <hvr@gnu.org>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This module's purpose is to provide a place for helper functions
|
||||
;; which are general enough to be usable by multiple modules and/or
|
||||
;; to alleviate circular module dependency problems.
|
||||
;;
|
||||
;; When possible, functions in this module shall be accompanied by
|
||||
;; ERT-based unit tests.
|
||||
;;
|
||||
;; See also `haskell-str.el' for string utility functions.
|
||||
;;
|
||||
;; All symbols in this module have a `haskell-utils-' prefix.
|
||||
|
||||
;;; Code:
|
||||
|
||||
;; =============================================================================
|
||||
;; NOTE:
|
||||
;; THIS MODULE IS SUPPOSED TO BE A LEAF-MODULE AND SHALL NOT REQUIRE/DEPEND-ON
|
||||
;; ANY OTHER HASKELL-MODE MODULES IN ORDER TO STAY AT THE BOTTOM OF THE MODULE
|
||||
;; DEPENDENCY GRAPH.
|
||||
;; =============================================================================
|
||||
|
||||
(eval-when-compile (require 'cl-lib))
|
||||
|
||||
(defvar-local haskell-utils-async-post-command-flag nil
|
||||
"Non-nil means some commands were triggered during async function execution.")
|
||||
|
||||
(defvar haskell-mode-interactive-prompt-state nil
|
||||
"Special variable indicating a state of user input waiting.")
|
||||
|
||||
(defun haskell-utils-read-directory-name (prompt default)
|
||||
"Read directory name and normalize to true absolute path.
|
||||
Refer to `read-directory-name' for the meaning of PROMPT and
|
||||
DEFAULT. If `haskell-process-load-or-reload-prompt' is nil,
|
||||
accept `default'."
|
||||
(let ((filename (file-truename (read-directory-name prompt default default))))
|
||||
(concat (replace-regexp-in-string "/$" "" filename) "/")))
|
||||
|
||||
(defun haskell-utils-parse-import-statement-at-point ()
|
||||
"Return imported module name if on import statement or nil otherwise.
|
||||
This currently assumes that the \"import\" keyword and the module
|
||||
name are on the same line.
|
||||
|
||||
This function supports the SafeHaskell and PackageImports syntax extensions.
|
||||
|
||||
Note: doesn't detect if in {--}-style comment."
|
||||
(save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(if (looking-at (concat "[\t ]*import[\t ]+"
|
||||
"\\(?:safe[\t ]+\\)?" ;; SafeHaskell
|
||||
"\\(?:qualified[\t ]+\\)?"
|
||||
"\\(?:\"[^\"]*\"[\t ]+\\)?" ;; PackageImports
|
||||
"\\([[:digit:][:upper:][:lower:]_.]+\\)"))
|
||||
(match-string-no-properties 1))))
|
||||
|
||||
(defun haskell-utils-async-update-post-command-flag ()
|
||||
"A special hook which collects triggered commands during async execution.
|
||||
This hook pushes value of variable `this-command' to flag variable
|
||||
`haskell-utils-async-post-command-flag'."
|
||||
(let* ((cmd this-command)
|
||||
(updated-flag (cons cmd haskell-utils-async-post-command-flag)))
|
||||
(setq haskell-utils-async-post-command-flag updated-flag)))
|
||||
|
||||
(defun haskell-utils-async-watch-changes ()
|
||||
"Watch for triggered commands during async operation execution.
|
||||
Resets flag variable
|
||||
`haskell-utils-async-update-post-command-flag' to NIL. By changes it is
|
||||
assumed that nothing happened, e.g. nothing was inserted in
|
||||
buffer, point was not moved, etc. To collect data `post-command-hook' is used."
|
||||
(setq haskell-utils-async-post-command-flag nil)
|
||||
(add-hook
|
||||
'post-command-hook #'haskell-utils-async-update-post-command-flag nil t))
|
||||
|
||||
(defun haskell-utils-async-stop-watching-changes (buffer)
|
||||
"Clean up after async operation finished.
|
||||
This function takes care about cleaning up things made by
|
||||
`haskell-utils-async-watch-changes'. The BUFFER argument is a buffer where
|
||||
`post-command-hook' should be disabled. This is neccessary, because
|
||||
it is possible that user will change buffer during async function
|
||||
execusion."
|
||||
(with-current-buffer buffer
|
||||
(setq haskell-utils-async-post-command-flag nil)
|
||||
(remove-hook
|
||||
'post-command-hook #'haskell-utils-async-update-post-command-flag t)))
|
||||
|
||||
(defun haskell-utils-reduce-string (str)
|
||||
"Remove newlines and extra whitespace from string STR.
|
||||
If line starts with a sequence of whitespaces, substitutes this
|
||||
sequence with a single whitespace. Removes all newline
|
||||
characters."
|
||||
(let ((s (replace-regexp-in-string "^\s+" " " str)))
|
||||
(replace-regexp-in-string "\r?\n" "" s)))
|
||||
|
||||
(defun haskell-utils-repl-response-error-status (response)
|
||||
"Parse response REPL's RESPONSE for errors.
|
||||
Returns one of the following symbols:
|
||||
|
||||
+ unknown-command
|
||||
+ option-missing
|
||||
+ interactive-error
|
||||
+ no-error
|
||||
|
||||
*Warning*: this funciton covers only three kind of responses:
|
||||
|
||||
* \"unknown command …\"
|
||||
REPL missing requested command
|
||||
* \"<interactive>:3:5: …\"
|
||||
interactive REPL error
|
||||
* \"Couldn't guess that module name. Does it exist?\"
|
||||
(:type-at and maybe some other commands error)
|
||||
* *all other reposnses* are treated as success reposneses and
|
||||
'no-error is returned."
|
||||
(if response
|
||||
(let ((first-line (car (split-string response "\n" t))))
|
||||
(cond
|
||||
((null first-line) 'no-error)
|
||||
((string-match-p "^unknown command" first-line)
|
||||
'unknown-command)
|
||||
((string-match-p
|
||||
"^Couldn't guess that module name. Does it exist?"
|
||||
first-line)
|
||||
'option-missing)
|
||||
((string-match-p "^<interactive>:" first-line)
|
||||
'interactive-error)
|
||||
(t 'no-error)))
|
||||
;; in case of nil-ish reponse it's not clear is it error response or not
|
||||
'no-error))
|
||||
|
||||
(defun haskell-utils-compose-type-at-command (pos)
|
||||
"Prepare :type-at command to be send to haskell process.
|
||||
POS is a cons cell containing min and max positions, i.e. target
|
||||
expression bounds."
|
||||
(save-excursion
|
||||
(let ((start-p (car pos))
|
||||
(end-p (cdr pos))
|
||||
start-l
|
||||
start-c
|
||||
end-l
|
||||
end-c
|
||||
value)
|
||||
(goto-char start-p)
|
||||
(setq start-l (line-number-at-pos))
|
||||
(setq start-c (1+ (current-column)))
|
||||
(goto-char end-p)
|
||||
(setq end-l (line-number-at-pos))
|
||||
(setq end-c (1+ (current-column)))
|
||||
(setq value (buffer-substring-no-properties start-p end-p))
|
||||
;; supress multiline expressions
|
||||
(let ((lines (split-string value "\n" t)))
|
||||
(when (and (cdr lines)
|
||||
(stringp (car lines)))
|
||||
(setq value (format "[ %s … ]" (car lines)))))
|
||||
(replace-regexp-in-string
|
||||
"\n$"
|
||||
""
|
||||
(format ":type-at %s %d %d %d %d %s"
|
||||
(buffer-file-name)
|
||||
start-l
|
||||
start-c
|
||||
end-l
|
||||
end-c
|
||||
value)))))
|
||||
|
||||
|
||||
(defun haskell-mode-toggle-interactive-prompt-state (&optional disabled)
|
||||
"Set `haskell-mode-interactive-prompt-state' to t.
|
||||
If given DISABLED argument sets variable value to nil, otherwise to t."
|
||||
(setq haskell-mode-interactive-prompt-state (not disabled)))
|
||||
|
||||
(provide 'haskell-utils)
|
||||
;;; haskell-utils.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell-utils.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell-utils.elc
Normal file
Binary file not shown.
528
elpa/haskell-mode-20171022.26/haskell.el
Normal file
528
elpa/haskell-mode-20171022.26/haskell.el
Normal file
@@ -0,0 +1,528 @@
|
||||
;;; haskell.el --- Top-level Haskell package -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright © 2014 Chris Done. All rights reserved.
|
||||
;; 2016 Arthur Fayzrakhmanov
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-hoogle)
|
||||
(require 'haskell-process)
|
||||
(require 'haskell-debug)
|
||||
(require 'haskell-interactive-mode)
|
||||
(require 'haskell-repl)
|
||||
(require 'haskell-load)
|
||||
(require 'haskell-commands)
|
||||
(require 'haskell-modules)
|
||||
(require 'haskell-string)
|
||||
(require 'haskell-completions)
|
||||
(require 'haskell-utils)
|
||||
(require 'haskell-customize)
|
||||
|
||||
(defvar interactive-haskell-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "C-c C-l") 'haskell-process-load-file)
|
||||
(define-key map (kbd "C-c C-r") 'haskell-process-reload)
|
||||
(define-key map (kbd "C-c C-t") 'haskell-process-do-type)
|
||||
(define-key map (kbd "C-c C-i") 'haskell-process-do-info)
|
||||
(define-key map (kbd "M-.") 'haskell-mode-jump-to-def-or-tag)
|
||||
(define-key map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
|
||||
(define-key map (kbd "C-c C-c") 'haskell-process-cabal-build)
|
||||
(define-key map (kbd "C-c v c") 'haskell-cabal-visit-file)
|
||||
(define-key map (kbd "C-c C-x") 'haskell-process-cabal)
|
||||
(define-key map (kbd "C-c C-b") 'haskell-interactive-switch)
|
||||
(define-key map (kbd "C-c C-z") 'haskell-interactive-switch)
|
||||
map)
|
||||
"Keymap for using `interactive-haskell-mode'.")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode interactive-haskell-mode
|
||||
"Minor mode for enabling haskell-process interaction."
|
||||
:lighter " Interactive"
|
||||
:keymap interactive-haskell-mode-map
|
||||
(add-hook 'completion-at-point-functions
|
||||
#'haskell-completions-sync-repl-completion-at-point
|
||||
nil
|
||||
t))
|
||||
|
||||
(make-obsolete 'haskell-process-completions-at-point
|
||||
'haskell-completions-sync-repl-completion-at-point
|
||||
"June 19, 2015")
|
||||
|
||||
(defun haskell-process-completions-at-point ()
|
||||
"A `completion-at-point' function using the current haskell process."
|
||||
(when (haskell-session-maybe)
|
||||
(let ((process (haskell-process))
|
||||
symbol-bounds)
|
||||
(cond
|
||||
;; ghci can complete module names, but it needs the "import "
|
||||
;; string at the beginning
|
||||
((looking-back (rx line-start
|
||||
"import" (1+ space)
|
||||
(? "qualified" (1+ space))
|
||||
(group (? (char upper) ; modid
|
||||
(* (char alnum ?' ?.)))))
|
||||
(line-beginning-position))
|
||||
(let ((text (match-string-no-properties 0))
|
||||
(start (match-beginning 1))
|
||||
(end (match-end 1)))
|
||||
(list start end
|
||||
(haskell-process-get-repl-completions process text))))
|
||||
;; Complete OPTIONS, a completion list comes from variable
|
||||
;; `haskell-ghc-supported-options'
|
||||
((and (nth 4 (syntax-ppss))
|
||||
(save-excursion
|
||||
(let ((p (point)))
|
||||
(and (search-backward "{-#" nil t)
|
||||
(search-forward-regexp "\\_<OPTIONS\\(?:_GHC\\)?\\_>" p t))))
|
||||
(looking-back
|
||||
(rx symbol-start "-" (* (char alnum ?-)))
|
||||
(line-beginning-position)))
|
||||
(list (match-beginning 0) (match-end 0) haskell-ghc-supported-options))
|
||||
;; Complete LANGUAGE, a list of completions comes from variable
|
||||
;; `haskell-ghc-supported-extensions'
|
||||
((and (nth 4 (syntax-ppss))
|
||||
(save-excursion
|
||||
(let ((p (point)))
|
||||
(and (search-backward "{-#" nil t)
|
||||
(search-forward-regexp "\\_<LANGUAGE\\_>" p t))))
|
||||
(setq symbol-bounds (bounds-of-thing-at-point 'symbol)))
|
||||
(list (car symbol-bounds) (cdr symbol-bounds)
|
||||
haskell-ghc-supported-extensions))
|
||||
((setq symbol-bounds (haskell-ident-pos-at-point))
|
||||
(cl-destructuring-bind (start . end) symbol-bounds
|
||||
(list start end
|
||||
(haskell-process-get-repl-completions
|
||||
process (buffer-substring-no-properties start end)))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-interactive-mode-return ()
|
||||
"Handle the return key."
|
||||
(interactive)
|
||||
(cond
|
||||
;; At a compile message, jump to the location of the error in the
|
||||
;; source.
|
||||
((haskell-interactive-at-compile-message)
|
||||
(next-error-internal))
|
||||
;; At the input prompt, handle the expression in the usual way.
|
||||
((haskell-interactive-at-prompt)
|
||||
(haskell-interactive-handle-expr))
|
||||
;; At any other location in the buffer, copy the line to the
|
||||
;; current prompt.
|
||||
(t
|
||||
(haskell-interactive-copy-to-prompt))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-kill (&optional leave-interactive-buffer)
|
||||
"Kill the session process and buffer, delete the session.
|
||||
0. Prompt to kill all associated buffers.
|
||||
1. Kill the process.
|
||||
2. Kill the interactive buffer unless LEAVE-INTERACTIVE-BUFFER is not given.
|
||||
3. Walk through all the related buffers and set their haskell-session to nil.
|
||||
4. Remove the session from the sessions list."
|
||||
(interactive)
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(let* ((session (haskell-session))
|
||||
(name (haskell-session-name session))
|
||||
(also-kill-buffers
|
||||
(and haskell-ask-also-kill-buffers
|
||||
(y-or-n-p
|
||||
(format "Killing `%s'. Also kill all associated buffers?"
|
||||
name)))))
|
||||
(haskell-kill-session-process session)
|
||||
(unless leave-interactive-buffer
|
||||
(kill-buffer (haskell-session-interactive-buffer session)))
|
||||
(cl-loop for buffer in (buffer-list)
|
||||
do (with-current-buffer buffer
|
||||
(when (and (boundp 'haskell-session)
|
||||
(string= (haskell-session-name haskell-session)
|
||||
name))
|
||||
(setq haskell-session nil)
|
||||
(when also-kill-buffers
|
||||
(kill-buffer)))))
|
||||
(setq haskell-sessions
|
||||
(cl-remove-if (lambda (session)
|
||||
(string= (haskell-session-name session)
|
||||
name))
|
||||
haskell-sessions)))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-interactive-kill ()
|
||||
"Kill the buffer and (maybe) the session."
|
||||
(interactive)
|
||||
(when (eq major-mode 'haskell-interactive-mode)
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(when (and (boundp 'haskell-session)
|
||||
haskell-session
|
||||
(y-or-n-p "Kill the whole session?"))
|
||||
(haskell-session-kill t)))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))
|
||||
|
||||
(defun haskell-session-make (name)
|
||||
"Make a Haskell session."
|
||||
(when (haskell-session-lookup name)
|
||||
(error "Session of name %s already exists!" name))
|
||||
(let ((session (setq haskell-session
|
||||
(list (cons 'name name)))))
|
||||
(add-to-list 'haskell-sessions session)
|
||||
(haskell-process-start session)
|
||||
session))
|
||||
|
||||
(defun haskell-session-new-assume-from-cabal ()
|
||||
"Prompt to create a new project based on a guess from the nearest Cabal file.
|
||||
If `haskell-process-load-or-reload-prompt' is nil, accept `default'."
|
||||
(let ((name (haskell-session-default-name)))
|
||||
(unless (haskell-session-lookup name)
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(if (or (not haskell-process-load-or-reload-prompt)
|
||||
(y-or-n-p (format "Start a new project named “%s”? " name)))
|
||||
(haskell-session-make name))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session ()
|
||||
"Get the Haskell session, prompt if there isn't one or fail."
|
||||
(or (haskell-session-maybe)
|
||||
(haskell-session-assign
|
||||
(or (haskell-session-from-buffer)
|
||||
(haskell-session-new-assume-from-cabal)
|
||||
(haskell-session-choose)
|
||||
(haskell-session-new)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-interactive-switch ()
|
||||
"Switch to the interactive mode for this session."
|
||||
(interactive)
|
||||
(let ((initial-buffer (current-buffer))
|
||||
(buffer (haskell-session-interactive-buffer (haskell-session))))
|
||||
(with-current-buffer buffer
|
||||
(setq haskell-interactive-previous-buffer initial-buffer))
|
||||
(unless (eq buffer (window-buffer))
|
||||
(switch-to-buffer-other-window buffer))))
|
||||
|
||||
(defun haskell-session-new ()
|
||||
"Make a new session."
|
||||
(let ((name (read-from-minibuffer "Project name: " (haskell-session-default-name))))
|
||||
(when (not (string= name ""))
|
||||
(let ((session (haskell-session-lookup name)))
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(if session
|
||||
(when
|
||||
(y-or-n-p
|
||||
(format "Session %s already exists. Use it?" name))
|
||||
session)
|
||||
(haskell-session-make name)))
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-session-change ()
|
||||
"Change the session for the current buffer."
|
||||
(interactive)
|
||||
(haskell-session-assign (or (haskell-session-new-assume-from-cabal)
|
||||
(haskell-session-choose)
|
||||
(haskell-session-new))))
|
||||
|
||||
(defun haskell-process-prompt-restart (process)
|
||||
"Prompt to restart the died PROCESS."
|
||||
(let ((process-name (haskell-process-name process))
|
||||
(cursor-in-echo-area t))
|
||||
(if haskell-process-suggest-restart
|
||||
(progn
|
||||
(haskell-mode-toggle-interactive-prompt-state)
|
||||
(unwind-protect
|
||||
(cond
|
||||
((string-match "You need to re-run the 'configure' command."
|
||||
(haskell-process-response process))
|
||||
(cl-case (read-char-choice
|
||||
(concat
|
||||
"The Haskell process ended. Cabal wants you to run "
|
||||
(propertize "cabal configure"
|
||||
'face
|
||||
'font-lock-keyword-face)
|
||||
" because there is a version mismatch. Re-configure (y, n, l: view log)?"
|
||||
"\n\n"
|
||||
"Cabal said:\n\n"
|
||||
(propertize (haskell-process-response process)
|
||||
'face
|
||||
'font-lock-comment-face))
|
||||
'(?l ?n ?y))
|
||||
(?y (let ((default-directory
|
||||
(haskell-session-cabal-dir
|
||||
(haskell-process-session process))))
|
||||
(message "%s"
|
||||
(shell-command-to-string "cabal configure"))))
|
||||
(?l (let* ((response (haskell-process-response process))
|
||||
(buffer (get-buffer "*haskell-process-log*")))
|
||||
(if buffer
|
||||
(switch-to-buffer buffer)
|
||||
(progn (switch-to-buffer
|
||||
(get-buffer-create "*haskell-process-log*"))
|
||||
(insert response)))))
|
||||
(?n)))
|
||||
(t
|
||||
(cl-case (read-char-choice
|
||||
(propertize
|
||||
(format "The Haskell process `%s' has died. Restart? (y, n, l: show process log) "
|
||||
process-name)
|
||||
'face
|
||||
'minibuffer-prompt)
|
||||
'(?l ?n ?y))
|
||||
(?y (haskell-process-start (haskell-process-session process)))
|
||||
(?l (let* ((response (haskell-process-response process))
|
||||
(buffer (get-buffer "*haskell-process-log*")))
|
||||
(if buffer
|
||||
(switch-to-buffer buffer)
|
||||
(progn (switch-to-buffer
|
||||
(get-buffer-create "*haskell-process-log*"))
|
||||
(insert response)))))
|
||||
(?n))))
|
||||
;; unwind
|
||||
(haskell-mode-toggle-interactive-prompt-state t)))
|
||||
(message "The Haskell process `%s' is dearly departed." process-name))))
|
||||
|
||||
(defun haskell-process ()
|
||||
"Get the current process from the current session."
|
||||
(haskell-session-process (haskell-session)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-kill-session-process (&optional session)
|
||||
"Kill the process."
|
||||
(interactive)
|
||||
(let* ((session (or session (haskell-session)))
|
||||
(existing-process (get-process (haskell-session-name session))))
|
||||
(when (processp existing-process)
|
||||
(haskell-interactive-mode-echo session "Killing process ...")
|
||||
(haskell-process-set (haskell-session-process session) 'is-restarting t)
|
||||
(delete-process existing-process))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-interactive-mode-visit-error ()
|
||||
"Visit the buffer of the current (or last) error message."
|
||||
(interactive)
|
||||
(with-current-buffer (haskell-session-interactive-buffer (haskell-session))
|
||||
(if (progn (goto-char (line-beginning-position))
|
||||
(looking-at haskell-interactive-mode-error-regexp))
|
||||
(progn (forward-line -1)
|
||||
(haskell-interactive-jump-to-error-line))
|
||||
(progn (goto-char (point-max))
|
||||
(haskell-interactive-mode-error-backward)
|
||||
(haskell-interactive-jump-to-error-line)))))
|
||||
|
||||
(defvar xref-prompt-for-identifier nil)
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-jump-to-tag (&optional next-p)
|
||||
"Jump to the tag of the given identifier.
|
||||
|
||||
Give optional NEXT-P parameter to override value of
|
||||
`xref-prompt-for-identifier' during definition search."
|
||||
(interactive "P")
|
||||
(let ((ident (haskell-string-drop-qualifier (haskell-ident-at-point)))
|
||||
(tags-file-dir (haskell-cabal--find-tags-dir))
|
||||
(tags-revert-without-query t))
|
||||
(when (and ident
|
||||
(not (string= "" (haskell-string-trim ident)))
|
||||
tags-file-dir)
|
||||
(let ((tags-file-name (concat tags-file-dir "TAGS")))
|
||||
(cond ((file-exists-p tags-file-name)
|
||||
(let ((xref-prompt-for-identifier next-p))
|
||||
(xref-find-definitions ident)))
|
||||
(t (haskell-mode-generate-tags ident)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-after-save-handler ()
|
||||
"Function that will be called after buffer's saving."
|
||||
(when haskell-tags-on-save
|
||||
(ignore-errors (haskell-mode-generate-tags))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-mode-tag-find (&optional _next-p)
|
||||
"The tag find function, specific for the particular session."
|
||||
(interactive "P")
|
||||
(cond
|
||||
((elt (syntax-ppss) 3) ;; Inside a string
|
||||
(haskell-mode-jump-to-filename-in-string))
|
||||
(t (call-interactively 'haskell-mode-jump-to-tag))))
|
||||
|
||||
(defun haskell-mode-jump-to-filename-in-string ()
|
||||
"Jump to the filename in the current string."
|
||||
(let* ((string (save-excursion
|
||||
(buffer-substring-no-properties
|
||||
(1+ (search-backward-regexp "\"" (line-beginning-position) nil 1))
|
||||
(1- (progn (forward-char 1)
|
||||
(search-forward-regexp "\"" (line-end-position) nil 1))))))
|
||||
(fp (expand-file-name string
|
||||
(haskell-session-cabal-dir (haskell-session)))))
|
||||
(find-file
|
||||
(read-file-name
|
||||
""
|
||||
fp
|
||||
fp))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-interactive-bring ()
|
||||
"Bring up the interactive mode for this session."
|
||||
(interactive)
|
||||
(let* ((session (haskell-session))
|
||||
(buffer (haskell-session-interactive-buffer session)))
|
||||
(pop-to-buffer buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-load-file ()
|
||||
"Load the current buffer file."
|
||||
(interactive)
|
||||
(save-buffer)
|
||||
(haskell-interactive-mode-reset-error (haskell-session))
|
||||
(haskell-process-file-loadish (format "load \"%s\"" (replace-regexp-in-string
|
||||
"\""
|
||||
"\\\\\""
|
||||
(buffer-file-name)))
|
||||
nil
|
||||
(current-buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-reload ()
|
||||
"Re-load the current buffer file."
|
||||
(interactive)
|
||||
(save-buffer)
|
||||
(haskell-interactive-mode-reset-error (haskell-session))
|
||||
(haskell-process-file-loadish "reload" t (current-buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-reload-file () (haskell-process-reload))
|
||||
|
||||
(make-obsolete 'haskell-process-reload-file 'haskell-process-reload
|
||||
"2015-11-14")
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-load-or-reload (&optional toggle)
|
||||
"Load or reload. Universal argument toggles which."
|
||||
(interactive "P")
|
||||
(if toggle
|
||||
(progn (setq haskell-reload-p (not haskell-reload-p))
|
||||
(message "%s (No action taken this time)"
|
||||
(if haskell-reload-p
|
||||
"Now running :reload."
|
||||
"Now running :load <buffer-filename>.")))
|
||||
(if haskell-reload-p (haskell-process-reload) (haskell-process-load-file))))
|
||||
|
||||
(make-obsolete 'haskell-process-load-or-reload 'haskell-process-load-file
|
||||
"2015-11-14")
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-cabal-build ()
|
||||
"Build the Cabal project."
|
||||
(interactive)
|
||||
(haskell-process-do-cabal "build")
|
||||
(haskell-process-add-cabal-autogen))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-cabal (p)
|
||||
"Prompts for a Cabal command to run."
|
||||
(interactive "P")
|
||||
(if p
|
||||
(haskell-process-do-cabal
|
||||
(read-from-minibuffer "Cabal command (e.g. install): "))
|
||||
(haskell-process-do-cabal
|
||||
(funcall haskell-completing-read-function "Cabal command: "
|
||||
(append haskell-cabal-commands
|
||||
(list "build --ghc-options=-fforce-recomp"))))))
|
||||
|
||||
(defun haskell-process-file-loadish (command reload-p module-buffer)
|
||||
"Run a loading-ish COMMAND that wants to pick up type errors\
|
||||
and things like that. RELOAD-P indicates whether the notification
|
||||
should say 'reloaded' or 'loaded'. MODULE-BUFFER may be used
|
||||
for various things, but is optional."
|
||||
(let ((session (haskell-session)))
|
||||
(haskell-session-current-dir session)
|
||||
(when haskell-process-check-cabal-config-on-load
|
||||
(haskell-process-look-config-changes session))
|
||||
(let ((process (haskell-process)))
|
||||
(haskell-process-queue-command
|
||||
process
|
||||
(make-haskell-command
|
||||
:state (list session process command reload-p module-buffer)
|
||||
:go (lambda (state)
|
||||
(haskell-process-send-string
|
||||
(cadr state) (format ":%s" (cl-caddr state))))
|
||||
:live (lambda (state buffer)
|
||||
(haskell-process-live-build
|
||||
(cadr state) buffer nil))
|
||||
:complete (lambda (state response)
|
||||
(haskell-process-load-complete
|
||||
(car state)
|
||||
(cadr state)
|
||||
response
|
||||
(cl-cadddr state)
|
||||
(cl-cadddr (cdr state)))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun haskell-process-minimal-imports ()
|
||||
"Dump minimal imports."
|
||||
(interactive)
|
||||
(unless (> (save-excursion
|
||||
(goto-char (point-min))
|
||||
(haskell-navigate-imports-go)
|
||||
(point))
|
||||
(point))
|
||||
(goto-char (point-min))
|
||||
(haskell-navigate-imports-go))
|
||||
(haskell-process-queue-sync-request (haskell-process)
|
||||
":set -ddump-minimal-imports")
|
||||
(haskell-process-load-file)
|
||||
(insert-file-contents-literally
|
||||
(concat (haskell-session-current-dir (haskell-session))
|
||||
"/"
|
||||
(haskell-guess-module-name-from-file-name (buffer-file-name))
|
||||
".imports")))
|
||||
|
||||
(defun haskell-interactive-jump-to-error-line ()
|
||||
"Jump to the error line."
|
||||
(let ((orig-line (buffer-substring-no-properties (line-beginning-position)
|
||||
(line-end-position))))
|
||||
(and (string-match "^\\([^:]+\\):\\([0-9]+\\):\\([0-9]+\\)\\(-[0-9]+\\)?:" orig-line)
|
||||
(let* ((file (match-string 1 orig-line))
|
||||
(line (match-string 2 orig-line))
|
||||
(col (match-string 3 orig-line))
|
||||
(session (haskell-interactive-session))
|
||||
(cabal-path (haskell-session-cabal-dir session))
|
||||
(src-path (haskell-session-current-dir session))
|
||||
(cabal-relative-file (expand-file-name file cabal-path))
|
||||
(src-relative-file (expand-file-name file src-path)))
|
||||
(let ((file (cond ((file-exists-p cabal-relative-file)
|
||||
cabal-relative-file)
|
||||
((file-exists-p src-relative-file)
|
||||
src-relative-file))))
|
||||
(when file
|
||||
(other-window 1)
|
||||
(find-file file)
|
||||
(haskell-interactive-bring)
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (string-to-number line)))
|
||||
(goto-char (+ (point) (string-to-number col) -1))
|
||||
(haskell-mode-message-line orig-line)
|
||||
t))))))
|
||||
|
||||
(provide 'haskell)
|
||||
;;; haskell.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/haskell.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/haskell.elc
Normal file
Binary file not shown.
106
elpa/haskell-mode-20171022.26/highlight-uses-mode.el
Normal file
106
elpa/haskell-mode-20171022.26/highlight-uses-mode.el
Normal file
@@ -0,0 +1,106 @@
|
||||
;;; highlight-uses-mode.el --- Mode for highlighting uses -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (c) 2014 Chris Done. All rights reserved.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar highlight-uses-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "TAB") 'highlight-uses-mode-next)
|
||||
(define-key map (kbd "S-TAB") 'highlight-uses-mode-prev)
|
||||
(define-key map (kbd "<backtab>") 'highlight-uses-mode-prev)
|
||||
(define-key map (kbd "RET") 'highlight-uses-mode-stop-here)
|
||||
(define-key map (kbd "C-g") 'highlight-uses-mode)
|
||||
map)
|
||||
"Keymap for using `highlight-uses-mode'.")
|
||||
|
||||
(defvar-local highlight-uses-mode-point nil)
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode highlight-uses-mode
|
||||
"Minor mode for highlighting and jumping between uses."
|
||||
:lighter " Uses"
|
||||
:keymap highlight-uses-mode-map
|
||||
(if highlight-uses-mode
|
||||
(setq highlight-uses-mode-point (point))
|
||||
(when highlight-uses-mode-point
|
||||
(goto-char highlight-uses-mode-point)))
|
||||
(remove-overlays (point-min) (point-max) 'highlight-uses-mode-highlight t))
|
||||
|
||||
(defun highlight-uses-mode-replace ()
|
||||
"Replace all highlighted instances in the buffer with something
|
||||
else."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((o (highlight-uses-mode-next)))
|
||||
(when o
|
||||
(let ((replacement (read-from-minibuffer (format "Replace uses %s with: "
|
||||
(buffer-substring
|
||||
(overlay-start o)
|
||||
(overlay-end o))))))
|
||||
|
||||
(while o
|
||||
(goto-char (overlay-start o))
|
||||
(delete-region (overlay-start o)
|
||||
(overlay-end o))
|
||||
(insert replacement)
|
||||
(setq o (highlight-uses-mode-next))))))))
|
||||
|
||||
(defun highlight-uses-mode-stop-here ()
|
||||
"Stop at this point."
|
||||
(interactive)
|
||||
(setq highlight-uses-mode-point (point))
|
||||
(highlight-uses-mode -1))
|
||||
|
||||
(defun highlight-uses-mode-next ()
|
||||
"Jump to next result."
|
||||
(interactive)
|
||||
(let ((os (sort (cl-remove-if (lambda (o)
|
||||
(or (<= (overlay-start o) (point))
|
||||
(not (overlay-get o 'highlight-uses-mode-highlight))))
|
||||
(overlays-in (point) (point-max)))
|
||||
(lambda (a b)
|
||||
(< (overlay-start a)
|
||||
(overlay-start b))))))
|
||||
(when os
|
||||
(goto-char (overlay-start (car os)))
|
||||
(car os))))
|
||||
|
||||
(defun highlight-uses-mode-prev ()
|
||||
"Jump to previous result."
|
||||
(interactive)
|
||||
(let ((os (sort (cl-remove-if (lambda (o)
|
||||
(or (>= (overlay-end o) (point))
|
||||
(not (overlay-get o 'highlight-uses-mode-highlight))))
|
||||
(overlays-in (point-min) (point)))
|
||||
(lambda (a b)
|
||||
(> (overlay-start a)
|
||||
(overlay-start b))))))
|
||||
(when os
|
||||
(goto-char (overlay-start (car os)))
|
||||
(car os))))
|
||||
|
||||
(defun highlight-uses-mode-highlight (start end)
|
||||
"Make a highlight overlay at the given span."
|
||||
(let ((o (make-overlay start end)))
|
||||
(overlay-put o 'priority 999)
|
||||
(overlay-put o 'face 'isearch)
|
||||
(overlay-put o 'highlight-uses-mode-highlight t)))
|
||||
|
||||
(provide 'highlight-uses-mode)
|
||||
BIN
elpa/haskell-mode-20171022.26/highlight-uses-mode.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/highlight-uses-mode.elc
Normal file
Binary file not shown.
262
elpa/haskell-mode-20171022.26/inf-haskell.el
Normal file
262
elpa/haskell-mode-20171022.26/inf-haskell.el
Normal file
@@ -0,0 +1,262 @@
|
||||
;;; inf-haskell.el --- Interaction with an inferior Haskell process -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 2017 Vasantha Ganesh Kanniappan <vasanthaganesh.k@tuta.io>
|
||||
|
||||
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
;; Keywords: Haskell
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; A major mode for the buffer that holds the inferior process
|
||||
|
||||
;; Todo:
|
||||
|
||||
;; - Check out Shim for ideas.
|
||||
;; - i-h-load-buffer and i-h-send-region.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'comint)
|
||||
(require 'shell) ; For directory tracking.
|
||||
(require 'etags)
|
||||
(require 'haskell-compat)
|
||||
(require 'compile)
|
||||
(require 'haskell-decl-scan)
|
||||
(require 'haskell-cabal)
|
||||
(require 'haskell-customize)
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-string)
|
||||
|
||||
;;;###autoload
|
||||
(defgroup inferior-haskell nil
|
||||
"Settings for REPL interaction via `inferior-haskell-mode'"
|
||||
:link '(custom-manual "(haskell-mode)inferior-haskell-mode")
|
||||
:prefix "inferior-haskell-"
|
||||
:prefix "haskell-"
|
||||
:group 'haskell)
|
||||
|
||||
(defcustom inferior-haskell-hook nil
|
||||
"The hook that is called after starting inf-haskell."
|
||||
:type 'hook)
|
||||
|
||||
(defun haskell-program-name-with-args ()
|
||||
"Return the command with the arguments to start the repl based on the
|
||||
directory structure."
|
||||
(cl-ecase (haskell-process-type)
|
||||
('ghci (cond ((eq system-type 'cygwin) (nconc "ghcii.sh"
|
||||
haskell-process-args-ghci))
|
||||
(t (nconc `(,haskell-process-path-ghci)
|
||||
haskell-process-args-ghci))))
|
||||
('cabal-repl (nconc `(,haskell-process-path-cabal
|
||||
"repl")
|
||||
haskell-process-args-cabal-repl))
|
||||
('stack-ghci (nconc `(,haskell-process-path-stack
|
||||
"ghci")
|
||||
haskell-process-args-stack-ghci))))
|
||||
|
||||
(defconst inferior-haskell-info-xref-re
|
||||
"-- Defined at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?$")
|
||||
|
||||
(defconst inferior-haskell-module-re
|
||||
"-- Defined in \\(.+\\)$"
|
||||
"Regular expression for matching module names in :info.")
|
||||
|
||||
(defvar inferior-haskell-multiline-prompt-re
|
||||
"^\\*?[[:upper:]][\\._[:alnum:]]*\\(?: \\*?[[:upper:]][\\._[:alnum:]]*\\)*| "
|
||||
"Regular expression for matching multiline prompt (the one inside :{ ... :} blocks).")
|
||||
|
||||
(defconst inferior-haskell-error-regexp-alist
|
||||
`(;; Format of error messages used by GHCi.
|
||||
("^\\(.+?\\):\\([0-9]+\\):\\(\\([0-9]+\\):\\)?\\( \\|\n *\\)\\([Ww]arning\\)?"
|
||||
1 2 4 ,@(if (fboundp 'compilation-fake-loc)
|
||||
'((6) nil (5 '(face nil font-lock-multiline t)))))
|
||||
;; Runtime exceptions, from ghci.
|
||||
("^\\*\\*\\* Exception: \\(.+?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\)): .*"
|
||||
1 ,@(if (fboundp 'compilation-fake-loc) '((2 . 4) (3 . 5)) '(2 3)))
|
||||
;; GHCi uses two different forms for line/col ranges, depending on
|
||||
;; whether it's all on the same line or not :-( In Emacs-23, I could use
|
||||
;; explicitly numbered subgroups to merge the two patterns.
|
||||
("^\\*\\*\\* Exception: \\(.+?\\):\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\): .*"
|
||||
1 2 ,(if (fboundp 'compilation-fake-loc) '(3 . 4) 3))
|
||||
;; Info messages. Not errors per se.
|
||||
,@(when (fboundp 'compilation-fake-loc)
|
||||
`(;; Other GHCi patterns used in type errors.
|
||||
("^[ \t]+at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)$"
|
||||
1 2 (3 . 4) 0)
|
||||
;; Foo.hs:318:80:
|
||||
;; Ambiguous occurrence `Bar'
|
||||
;; It could refer to either `Bar', defined at Zork.hs:311:5
|
||||
;; or `Bar', imported from Bars at Frob.hs:32:0-16
|
||||
;; (defined at Location.hs:97:5)
|
||||
("[ (]defined at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\))?$" 1 2 3 0)
|
||||
("imported from .* at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)$"
|
||||
1 2 (3 . 4) 0)
|
||||
;; Info xrefs.
|
||||
(,inferior-haskell-info-xref-re 1 2 (3 . 4) 0))))
|
||||
"Regexps for error messages generated by inferior Haskell processes.
|
||||
The format should be the same as for `compilation-error-regexp-alist'.")
|
||||
|
||||
(defconst haskell-prompt-regexp
|
||||
;; Why the backslash in [\\._[:alnum:]]?
|
||||
"^\\*?[[:upper:]][\\._[:alnum:]]*\\(?: \\*?[[:upper:]][\\._[:alnum:]]*\\)*\\( λ\\)?> \\|^λ?> $")
|
||||
|
||||
;;; TODO
|
||||
;;; -> Make font lock work for strings, directories, hyperlinks
|
||||
;;; -> Make font lock work for key words???
|
||||
|
||||
(defvar inf-haskell-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "\C-c\C-d" 'comint-kill-subjob)
|
||||
map))
|
||||
|
||||
(defvaralias 'inferior-haskell-mode-map 'inf-haskell-map)
|
||||
|
||||
(define-derived-mode inferior-haskell-mode comint-mode "Inf-Haskell"
|
||||
"Major mode for interacting with an inferior Haskell process."
|
||||
:group 'inferior-haskell
|
||||
(setq-local comint-prompt-regexp haskell-prompt-regexp)
|
||||
|
||||
(setq-local paragraph-start haskell-prompt-regexp)
|
||||
|
||||
(setq-local comint-input-autoexpand nil)
|
||||
(setq-local comint-prompt-read-only t)
|
||||
|
||||
;; Setup directory tracking.
|
||||
(setq-local shell-cd-regexp ":cd")
|
||||
(condition-case nil
|
||||
(shell-dirtrack-mode 1)
|
||||
(error ;The minor mode function may not exist or not accept an arg.
|
||||
(setq-local shell-dirtrackp t)
|
||||
(add-hook 'comint-input-filter-functions 'shell-directory-tracker
|
||||
nil 'local)))
|
||||
|
||||
;; Setup `compile' support so you can just use C-x ` and friends.
|
||||
(setq-local compilation-error-regexp-alist inferior-haskell-error-regexp-alist)
|
||||
(setq-local compilation-first-column 0) ;GHCI counts from 0.
|
||||
(if (and (not (boundp 'minor-mode-overriding-map-alist))
|
||||
(fboundp 'compilation-shell-minor-mode))
|
||||
;; If we can't remove compilation-minor-mode bindings, at least try to
|
||||
;; use compilation-shell-minor-mode, so there are fewer
|
||||
;; annoying bindings.
|
||||
(compilation-shell-minor-mode 1)
|
||||
;; Else just use compilation-minor-mode but without its bindings because
|
||||
;; things like mouse-2 are simply too annoying.
|
||||
(compilation-minor-mode 1)
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(dolist (keys '([menu-bar] [follow-link]))
|
||||
;; Preserve some of the bindings.
|
||||
(define-key map keys (lookup-key compilation-minor-mode-map keys)))
|
||||
(add-to-list 'minor-mode-overriding-map-alist
|
||||
(cons 'compilation-minor-mode map))))
|
||||
(add-hook 'inferior-haskell-hook 'inferior-haskell-init))
|
||||
|
||||
(defvar inferior-haskell-buffer nil
|
||||
"The buffer in which the inferior process is running.")
|
||||
|
||||
(defun inferior-haskell-start-process ()
|
||||
"Start an inferior haskell process.
|
||||
With universal prefix \\[universal-argument], prompts for a COMMAND,
|
||||
otherwise uses `haskell-program-name-with-args'.
|
||||
It runs the hook `inferior-haskell-hook' after starting the process and
|
||||
setting up the inferior-haskell buffer."
|
||||
(let ((command (haskell-program-name-with-args)))
|
||||
(setq default-directory inferior-haskell-root-dir)
|
||||
(setq inferior-haskell-buffer
|
||||
(apply 'make-comint "haskell" (car command) nil (cdr command)))
|
||||
(with-current-buffer inferior-haskell-buffer
|
||||
(inferior-haskell-mode)
|
||||
(run-hooks 'inferior-haskell-hook))))
|
||||
|
||||
(defun inferior-haskell-process ()
|
||||
"Restart if not present."
|
||||
(cond ((and (buffer-live-p inferior-haskell-buffer)
|
||||
(comint-check-proc inferior-haskell-buffer))
|
||||
(get-buffer-process inferior-haskell-buffer))
|
||||
(t (inferior-haskell-start-process)
|
||||
(inferior-haskell-process))))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'run-haskell 'switch-to-haskell)
|
||||
;;;###autoload
|
||||
(defun switch-to-haskell ()
|
||||
"Show the inferior-haskell buffer. Start the process if needed."
|
||||
(interactive)
|
||||
(let ((proc (inferior-haskell-process)))
|
||||
(pop-to-buffer-same-window (process-buffer proc))))
|
||||
|
||||
(defvar inferior-haskell-result-history nil)
|
||||
|
||||
(defvar haskell-next-input ""
|
||||
"This is a temporary variable to store the intermediate results while
|
||||
`accecpt-process-output' with `haskell-extract-exp'")
|
||||
|
||||
(defun haskell-extract-exp (str)
|
||||
(setq haskell-next-input (concat haskell-next-input str))
|
||||
(if (with-temp-buffer
|
||||
(insert haskell-next-input)
|
||||
(re-search-backward haskell-prompt-regexp nil t 1))
|
||||
(progn
|
||||
(push (substring haskell-next-input
|
||||
0
|
||||
(1- (with-temp-buffer
|
||||
(insert haskell-next-input)
|
||||
(re-search-backward haskell-prompt-regexp nil t 1))))
|
||||
inferior-haskell-result-history)
|
||||
(setq haskell-next-input ""))
|
||||
""))
|
||||
|
||||
(defun inferior-haskell-no-result-return (strg)
|
||||
(let ((proc (inferior-haskell-process)))
|
||||
(with-local-quit
|
||||
(progn
|
||||
(add-to-list 'comint-preoutput-filter-functions
|
||||
(lambda (output)
|
||||
(haskell-extract-exp output)))
|
||||
(process-send-string proc strg)
|
||||
(accept-process-output proc)
|
||||
(sit-for 0.1)
|
||||
(setq comint-preoutput-filter-functions nil)))))
|
||||
|
||||
(defun inferior-haskell-get-result (inf-expr)
|
||||
"Submit the expression `inf-expr' to ghci and read the result."
|
||||
(let* ((times 5))
|
||||
(inferior-haskell-no-result-return (concat inf-expr "\n"))
|
||||
(while (and (> times 0)
|
||||
(not (stringp (car inferior-haskell-result-history))))
|
||||
(setq times (1- times))
|
||||
(inferior-haskell-no-result-return (concat inf-expr "\n")))
|
||||
(haskell-string-chomp (car inferior-haskell-result-history))))
|
||||
|
||||
(defun inferior-haskell-init ()
|
||||
"The first thing run while initalizing inferior-haskell-buffer"
|
||||
(with-local-quit
|
||||
(with-current-buffer inferior-haskell-buffer
|
||||
(process-send-string (inferior-haskell-process) "\n")
|
||||
(accept-process-output (inferior-haskell-process))
|
||||
(sit-for 0.1))))
|
||||
|
||||
(defvar haskell-set+c-p nil
|
||||
"t if `:set +c` else nil")
|
||||
|
||||
(defun haskell-set+c ()
|
||||
"set `:set +c` is not already set"
|
||||
(if (not haskell-set+c-p)
|
||||
(inferior-haskell-get-result ":set +c")))
|
||||
|
||||
(provide 'inf-haskell)
|
||||
|
||||
;;; inf-haskell.el ends here
|
||||
BIN
elpa/haskell-mode-20171022.26/inf-haskell.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/inf-haskell.elc
Normal file
Binary file not shown.
16
elpa/haskell-mode-20171022.26/logo.svg
Normal file
16
elpa/haskell-mode-20171022.26/logo.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<defs>
|
||||
<clipPath id="clip1">
|
||||
<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 "/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="surface0">
|
||||
<g clip-path="url(#clip1)" clip-rule="nonzero">
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
190
elpa/haskell-mode-20171022.26/w3m-haddock.el
Normal file
190
elpa/haskell-mode-20171022.26/w3m-haddock.el
Normal file
@@ -0,0 +1,190 @@
|
||||
;;; -*- lexical-binding: t -*-
|
||||
;;; w3m-haddock.el --- Make browsing haddocks with w3m-mode better.
|
||||
|
||||
;; Copyright (C) 2014 Chris Done
|
||||
|
||||
;; Author: Chris Done <chrisdone@gmail.com>
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This file is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 3, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; This file is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'haskell-mode)
|
||||
(require 'haskell-font-lock)
|
||||
|
||||
(declare-function w3m-buffer-title "ext:w3m")
|
||||
(declare-function w3m-browse-url "ext:w3m")
|
||||
(defvar w3m-current-url)
|
||||
|
||||
(add-hook 'w3m-display-hook 'w3m-haddock-display)
|
||||
|
||||
;;;###autoload
|
||||
(defface w3m-haddock-heading-face
|
||||
'((((class color)) :inherit highlight))
|
||||
"Face for quarantines."
|
||||
:group 'haskell)
|
||||
|
||||
(defcustom haskell-w3m-haddock-dirs
|
||||
'("~/.cabal/share/doc/")
|
||||
"The path to your cabal documentation dir. It should contain
|
||||
directories of package-name-x.x.
|
||||
|
||||
You can rebind this if you're using hsenv by adding it to your
|
||||
.dir-locals.el in your project root. E.g.
|
||||
|
||||
((haskell-mode . ((haskell-w3m-haddock-dirs . (\"/home/chris/Projects/foobar/.hsenv/cabal/share/doc\")))))
|
||||
|
||||
"
|
||||
:group 'haskell
|
||||
:type 'list)
|
||||
|
||||
(defvar w3m-haddock-entry-regex "^\\(\\(data\\|type\\) \\|[a-z].* :: \\)"
|
||||
"Regex to match entry headings.")
|
||||
|
||||
(defun haskell-w3m-open-haddock ()
|
||||
"Open a haddock page in w3m."
|
||||
(interactive)
|
||||
(let* ((entries (cl-remove-if (lambda (s) (string= s ""))
|
||||
(apply 'append (mapcar (lambda (dir)
|
||||
(split-string (shell-command-to-string (concat "ls -1 " dir))
|
||||
|
||||
"\n"))
|
||||
haskell-w3m-haddock-dirs))))
|
||||
(package-dir (ido-completing-read
|
||||
"Package: "
|
||||
entries)))
|
||||
(cond
|
||||
((member package-dir entries)
|
||||
(unless (cl-loop for dir in haskell-w3m-haddock-dirs
|
||||
when (w3m-haddock-find-index dir package-dir)
|
||||
do (progn (w3m-browse-url (w3m-haddock-find-index dir package-dir)
|
||||
t)
|
||||
(cl-return t)))
|
||||
(w3m-browse-url (concat "http://hackage.haskell.org/package/"
|
||||
package-dir)
|
||||
t)))
|
||||
(t
|
||||
(w3m-browse-url (concat "http://hackage.haskell.org/package/"
|
||||
package-dir)
|
||||
t)))))
|
||||
|
||||
(defun w3m-haddock-find-index (dir package)
|
||||
(let ((html-index (concat dir "/" package "/html/index.html"))
|
||||
(index (concat dir "/" package "/index.html")))
|
||||
(cond
|
||||
((file-exists-p html-index)
|
||||
html-index)
|
||||
((file-exists-p index)
|
||||
index))))
|
||||
|
||||
(defun w3m-haddock-page-p ()
|
||||
"Haddock general page?"
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(forward-line -2)
|
||||
(looking-at "[ ]*Produced by Haddock")))
|
||||
|
||||
(defun w3m-haddock-source-p ()
|
||||
"Haddock source page?"
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(or (looking-at "Location: https?://hackage.haskell.org/package/.*/docs/src/")
|
||||
(looking-at "Location: file://.*cabal/share/doc/.*/html/src/")
|
||||
(looking-at "Location: .*src/.*.html$"))))
|
||||
|
||||
(defun w3m-haddock-p ()
|
||||
"Any haddock page?"
|
||||
(or (w3m-haddock-page-p)
|
||||
(w3m-haddock-source-p)))
|
||||
|
||||
(defun w3m-haddock-find-tag ()
|
||||
"Find a tag by jumping to the \"All\" index and doing a
|
||||
search-forward."
|
||||
(interactive)
|
||||
(when (w3m-haddock-p)
|
||||
(let ((ident (haskell-ident-at-point)))
|
||||
(when ident
|
||||
(w3m-browse-url
|
||||
(replace-regexp-in-string "docs/.*" "docs/doc-index-All.html" w3m-current-url))
|
||||
(search-forward ident)))))
|
||||
|
||||
(defun w3m-haddock-display (_url)
|
||||
"To be run by w3m's display hook. This takes a normal w3m
|
||||
buffer containing hadddock documentation and reformats it to be
|
||||
more usable and look like a dedicated documentation page."
|
||||
(when (w3m-haddock-page-p)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region (point)
|
||||
(line-end-position))
|
||||
(w3m-haddock-next-heading)
|
||||
;; Start formatting entries
|
||||
(while (looking-at w3m-haddock-entry-regex)
|
||||
(when (w3m-haddock-valid-heading)
|
||||
(w3m-haddock-format-heading))
|
||||
(w3m-haddock-next-heading))))
|
||||
(rename-buffer (concat "*haddock: " (w3m-buffer-title (current-buffer)) "*")))
|
||||
(when (w3m-haddock-source-p)
|
||||
(font-lock-mode -1)
|
||||
(let ((n (line-number-at-pos)))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(forward-line 1)
|
||||
(let ((text (buffer-substring (point)
|
||||
(point-max)))
|
||||
(inhibit-read-only t))
|
||||
(delete-region (point)
|
||||
(point-max))
|
||||
(insert
|
||||
(haskell-fontify-as-mode text
|
||||
'haskell-mode))))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- n)))))
|
||||
|
||||
(defun w3m-haddock-format-heading ()
|
||||
"Format a haddock entry."
|
||||
(let ((o (make-overlay (line-beginning-position)
|
||||
(1- (save-excursion (w3m-haddock-header-end))))))
|
||||
(overlay-put o 'face 'w3m-haddock-heading-face))
|
||||
(let ((end (save-excursion
|
||||
(w3m-haddock-next-heading)
|
||||
(when (w3m-haddock-valid-heading)
|
||||
(point)))))
|
||||
(when end
|
||||
(save-excursion
|
||||
(w3m-haddock-header-end)
|
||||
(indent-rigidly (point)
|
||||
end
|
||||
4)))))
|
||||
|
||||
(defun w3m-haddock-next-heading ()
|
||||
"Go to the next heading, or end of the buffer."
|
||||
(forward-line 1)
|
||||
(or (search-forward-regexp w3m-haddock-entry-regex nil t 1)
|
||||
(goto-char (point-max)))
|
||||
(goto-char (line-beginning-position)))
|
||||
|
||||
(defun w3m-haddock-valid-heading ()
|
||||
"Is this a valid heading?"
|
||||
(not (get-text-property (point) 'face)))
|
||||
|
||||
(defun w3m-haddock-header-end ()
|
||||
"Go to the end of the header."
|
||||
(search-forward-regexp "\n[ \n]"))
|
||||
|
||||
(provide 'w3m-haddock)
|
||||
BIN
elpa/haskell-mode-20171022.26/w3m-haddock.elc
Normal file
BIN
elpa/haskell-mode-20171022.26/w3m-haddock.elc
Normal file
Binary file not shown.
Reference in New Issue
Block a user