Code Monkey home page Code Monkey logo

haskell-vim's Introduction

Syntax Highlighting and Indentation for Haskell and Cabal

I was unhappy with the Haskell scripts that are shipped with vim, therefore I decided to make my own based on idris-vim.

I hope you find this useful.

Screenshot

Features

  • Covers a broader spectrum of keywords
  • Highlighting for new features like type families, pattern synonyms, arrow syntax, recursive do, role annotations, QuasiQuotation
  • More contextual highlighting (e.g. highlight 'as' or 'family' only in appropriate places)
  • Smarter indentation
  • Better Cabal support

Installation

I recommend using Pathogen for installation. Simply clone this repo into your ~/.vim/bundle directory and you are ready to go.

cd ~/.vim/bundle
git clone https://github.com/neovimhaskell/haskell-vim.git

Be sure that the following lines are in your .vimrc

syntax on
filetype plugin indent on

Manual Installation

Copy content into your ~/.vim directory.

Configuration

Features

To enable the features you would like to use, just add the according line to your .vimrc.

let g:haskell_enable_quantification = 1   " to enable highlighting of `forall`
let g:haskell_enable_recursivedo = 1      " to enable highlighting of `mdo` and `rec`
let g:haskell_enable_arrowsyntax = 1      " to enable highlighting of `proc`
let g:haskell_enable_pattern_synonyms = 1 " to enable highlighting of `pattern`
let g:haskell_enable_typeroles = 1        " to enable highlighting of type roles
let g:haskell_enable_static_pointers = 1  " to enable highlighting of `static`
let g:haskell_backpack = 1                " to enable highlighting of backpack keywords

Highlighting

haskell-vim has an opinionated highlighting. If you do not like that you can switch to a more traditional mode by setting g:haskell_classic_highlighting to 1.

Disabling Template Haskell and Quasiquoting syntax is possible by setting g:haskell_disable_TH to 1.

Indentation

To configure indentation in haskell-vim you can use the following variables to change indentation depth, just add the according line to your .vimrc.

If you dislike how indentation works you can disable it by setting g:haskell_indent_disable to 1.

Additionally you can use the vim-hindent plugin to achieve automatic indentation using hindent.

Haskell

  • let g:haskell_indent_if = 3

      if bool
      >>>then ...
      >>>else ...
    
  • let g:haskell_indent_case = 2

      case xs of
      >>[]     -> ...
      >>(y:ys) -> ...
    
  • let g:haskell_indent_let = 4

      let x = 0 in
      >>>>x
    
  • let g:haskell_indent_where = 6

      where f :: Int -> Int
      >>>>>>f x = x
    
  • let g:haskell_indent_before_where = 2

      foo
      >>where
    
  • let g:haskell_indent_after_bare_where = 2

      where
      >>foo
    
  • let g:haskell_indent_do = 3

      do x <- a
      >>>y <- b
    
  • let g:haskell_indent_in = 1

      let x = 1
      >in x
    
  • let g:haskell_indent_guard = 2

      f x y
      >>|
    

haskell-vim also supports an alterative style for case indentation.

  • let g:haskell_indent_case_alternative = 1

      f xs ys = case xs of
      >>[]     -> ...
      >>(y:ys) -> ...
    

Cabal

  • let g:cabal_indent_section = 2 (limited to max. 4 spaces)

     executable name
     >>main-is:             Main.hs
    

haskell-vim's People

Contributors

alx741 avatar chkno avatar cohama avatar duckwork avatar epsilonhalbe avatar exallium avatar expipiplus1 avatar hustmphrrr avatar isti115 avatar jdagilliland avatar jez avatar johntyree avatar mheinzel avatar michalt avatar mmwtsn avatar osa1 avatar raichoo avatar ruedigerha avatar sbdchd avatar tomsmalley avatar yous avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

haskell-vim's Issues

indentation error on 'case...of...'

hi, I'm reading Real World Haskell, and I'm using this script to help me handle the indent.

But I've got an issue at line 8 :

splitLines [] = []
splitLines cs =
    let (pre, suf) = break isTerminator cs
     in pre: case suf of
               ('\r': '\n': rest) -> splitLines rest
               ('\r': rest) -> splitLines rest
               ('\n': rest) -> splitLines rest
             _ -> []

isTerminator c = c == '\r' || c == '\n'

_ -> [] is in a bad indent level.

Thanks!

Highlight error

When work with multiply line text, ghc seems to picks up line escape first. While vim + haskell-vim picks up regular escape first. Following example works in with ghc, but haskell-vim treats "in undefined" as part of the str, too.

let str = "a\
\b\
\"
in undefined

Character literals are not highlighted correctly

The following code

main = print $ '"' : foo
  where foo = "bar"

is highlighted like this:
screen shot 2015-03-16 at 6 26 19 pm
with the string not being terminated at the second quote. When I change what's in the single quotes to anything else, the string problem goes away, but I can't seem to get anything to be highlighted as "haskellChar".

Highlighting "TODO", "FIXME", etc.

It seems like most language syntax files do this, including vim2hs. I'm no vimscript pro, but I think haskell-vim doesn't highlight Todo syntax group, so even if I add this to my .vimrc:

syn match   myTodo   contained   "\<\(TODO\|FIXME\):"
hi def link myTodo Todo

It still doesn't highlight. Adding this would be really awesome.

Don't indent after let in do-block

.......................................................... $ do
    a <- ...
    b <- ...
    let c = ...
        |<--cursor here

Also congrats on being the only haskell syntax highlighter that doesn't choke (slow to a crawl) on match paren. I think this is because () and [] aren't being scanned for tuple/list constructor highlighting.

'where' and 'let..in'

first of all thanks for your effort.

two things I noticed using haskell-vim, let's start with 'where', consider the following:

add1 x = x + y
where
    y = 1

your script won't indent the where which cause a parse error, it should get indented like this:

add1 x = x + y
    where
        y = 1

and the let..in, ideally, when you type in it should get back to the let column.
what's happening with haskell-vim is this:

add2 x = let y = 1
             z = 1
             in = x + y + z

which is fine by the compiler, but it's not aligned.

multi-line comment formatting is not working as good

Suppose I have this comment block:

--    If they're in form `C1 t1_1 .. t1_n` and `C2 t2_1 .. t2_m` (C1 and C2 are
--    type constructors), it expands C1 and C2 if they're different type synonyms.
--    Then it recursively does the same thing on expanded types. If C1 and C2 are
--    same, then it should apply same procedure to arguments of C1 and arguments
--    of C2 to make them as similar as possible.

Some of these lines are longer than I like so I choose the whole thing and do gq and it's formatted perfectly:

--    If they're in form `C1 t1_1 .. t1_n` and `C2 t2_1 .. t2_m` (C1 and C2 are
--    type constructors), it expands C1 and C2 if they're different type
--    synonyms. Then it recursively does the same thing on expanded types. If C1
--    and C2 are same, then it should apply same procedure to arguments of C1
--    and arguments of C2 to make them as similar as possible.

Splitting worked great and new line is indented correctly.

However, if I do the same thing on this comment block:

{-
      If they're in form `C1 t1_1 .. t1_n` and `C2 t2_1 .. t2_m` (C1 and C2 are
      type constructors), it expands C1 and C2 if they're different type synonyms.
      Then it recursively does the same thing on expanded types. If C1 and C2 are
      same, then it should apply same procedure to arguments of C1 and arguments
      of C2 to make them as similar as possible.
-}

It becomes this:

{-
      If they're in form `C1 t1_1 .. t1_n` and `C2 t2_1 .. t2_m` (C1 and C2 are
 type constructors), it expands C1 and C2 if they're different type synonyms.
 Then it recursively does the same thing on expanded types. If C1 and C2 are
 same, then it should apply same procedure to arguments of C1 and arguments of
 C2 to make them as similar as possible.
-}

Indentation is completely messed up. There's another problem which may be related. Suppose I don't have any indentation, like so:

{-
If they're in form `C1 t1_1 .. t1_n` and `C2 t2_1 .. t2_m` (C1 and C2 are type
constructors), it expands C1 and C2 if they're different type synonyms. Then it
recursively does the same thing on expanded types. If C1 and C2 are same, then
it should apply same procedure to arguments of C1 and arguments of C2 to make
them as similar as possible.
-}

gq again messes things up by generating this:

{-
If they're in form `C1 t1_1 .. t1_n` and `C2 t2_1 .. t2_m` (C1 and C2 are type
 constructors), it expands C1 and C2 if they're different type synonyms. Then it
 recursively does the same thing on expanded types. If C1 and C2 are same, then
 it should apply same procedure to arguments of C1 and arguments of C2 to make
 them as similar as possible.
-}

For some reason every line except the first one is indented by one space.

It'd be really awesome if multi-line comments would work exactly the way single-line comments works.

Ignore indentation inside comments when auto-wrapping long lines

It seems that parentheses are not "ignored" when wrapping long comment lines. E.g. if I enter the following comment (with textwidth=79) as a single line and use gqq to re-format the line, it is indented strangely:

-- This is a very long comment that happens to have a (section within
                                                      -- parenthesis, where the
                                                                            -- wrapping
                                                                            -- is
                                                                            -- slightly
                                                                            -- weird!)

I'd like the indentation to ignore the parenthesis, and wrap the comment as follows:

-- This is a very long comment that happens to have a (section within
-- parenthesis, where the wrapping is slightly weird!)

Please distinguish pragmas from comment blocks

I'm using the neco-ghc plugin which allows completion of some Haskell pragmas (LANGUAGE, OPTIONS_GHC), but the completion doesn't work with this syntax because neco-ghc disables completion where the current syntax name contains "Comment" (as is the case here).

Besides, I like a visual distinction between comments and pragmas (the Haskell syntax plugin I previously used did this).

Usage of ftdetect

Regarding your recent commit e8a3cb0, are you sure that the file in ftdetect is supposed to set local options? I thought it was only for determining the filetype (and I think the default detection is fine) and that options should go into ftplugin.

The point is, I occasionally start a new unnamed buffer and just do a set ft=haskell. The setlocal calls from ftdetect then won't become active until I've written and reloaded that file with a *.hs filename. I'm worried a bit about options set differently depending on how I started working on a Haskell file.

Actually smart tabbing

Vim's python's syntax file sets softtabstop to shiftwidth (or something equivalent). This won't work for haskell, but this might:

function! SoftBackspace()
python << endpython
import vim
# get current indent level
# get previous indent level
# get softtabstop
# get current position
# get subsequent softtabstop multiple
# if    softtabstop > 0 and
#       current_position < current_indent_level and
#       next_sts_pos < prev_indent_level < current_position:
#           dedent to prev_indent_level
# else:
#       call regular backspace
# if previous indent level within softtabstop:
def indent_of(line, tabsize):
    line = line.expandtabs(tabsize)
    return len(line) - len(line.lstrip())
# find next lower level index
# go back one line at a time, for a max of ... 50?
# stop condition: indent is less than current indent
#   return target indent
# stop condition: current indent is greater than previous indent (excluding current line)
#   bad idea -> multiple if clauses have plausible reindents. DO NOT DO THIS
# reach end of segment
#   fallback upon regular BS behaviour
#   trigger by returning 0
def previous_indent_first(idx, tabsize, cindent, limit=50):
    while idx>=0 and limit>0:
        line = vim.current.buffer[idx]
        if not line.isalpha():
            lindent = indent_of(line, tabsize)
            if lindent < cindent:
                return lindent
        idx -= 1
        limit -= 1
    return 0
def previous_indent_nearest(idx, tabsize, cindent, limit=50):
    indents = [0]
    while idx>=0 and limit>0:
        line = vim.current.buffer[idx]
        if not line.isalpha():
            idents.append(indent_of(line, tabsize))
        idx -= 1
        limit -= 1
    return min(abs(cindent-i) for i in indents)
def elide(string, start, stop):
    return string[:start] + string[stop:]
softtab = int(vim.eval("&softtabstop"))
row,col = vim.current.window.cursor
cindent = indent_of(vim.current.line, softtab)
pindent = previous_indent_first(row-2, softtab, cindent)
stsdent = divmod(col, softtab)[0] * softtab
if  (   softtab > 0
    and col <= cindent
    and stsdent < pindent < col
    ):
    vim.current.line = elide(vim.current.line, pindent, col)
else:
    vim.command(":normal! i\b")
endpython
endfunction


function! SoftTab()
python << endpython
import vim
endpython
endfunction


set lazyredraw


au FileType haskell inoremap <buffer> <silent> <BS>  <C-o>:call SoftDedentBackspace()<CR>
au FileType haskell inoremap <buffer> <silent> <Tab> <C-o>:call SoftDedentTab()<CR>

I'm posting this because

  • tangentially related to this project

Currently, not finished.

Joining single comment lines doesn't remove comment indicators

In a buffer containing the following (short) comment lines:

-- This comment
-- should be on one line.

If I move to the top line and join the lines using J I get:

-- This comment -- should be on one line.

Rather than the desired:

-- This comment should be on one line.

indent setting is not functioning?

in my .vim/ftplugin/haskell.vim i have following settings:

let g:haskell_enable_quantification   = 1 " to enable highlighting of forall
let g:haskell_enable_recursivedo      = 1 " to enable highlighting of mdo and rec
let g:haskell_enable_arrowsyntax      = 1 " to enable highlighting of proc
let g:haskell_enable_pattern_synonyms = 1 " to enable highlighting of pattern
let g:haskell_enable_typeroles        = 1 " to enable highlighting of type roles
let g:haskell_enable_static_pointers  = 1 " to enable highlighting of static
let g:haskell_indent_if               = 3
let g:haskell_indent_case             = 2
let g:haskell_indent_let              = 4
let g:haskell_indent_where            = 6
let g:haskell_indent_do               = 3
let g:haskell_indent_in               = 1

however, it's not working. for example,

   if bool
|<- curcor is here
      | instead of here

I use pathogen to manage my plugins, and here are the plugins i currently have:

$ ls bundle/
ghcmod-vim  haskell-vim  neco-ghc  nerdtree  python-mode  syntastic  tabular  taglist  vimerl  vim-fugitive  vim-markdown  vimproc.vim

is there any possible conflict between them?

however, there seems to be no extra ftplugin for haskell:

$ find bundle/ -name 'haskell.vim' -type f
bundle/haskell-vim/ftplugin/haskell.vim
bundle/haskell-vim/syntax/haskell.vim
bundle/haskell-vim/ftdetect/haskell.vim
bundle/haskell-vim/indent/haskell.vim
bundle/haskell-vim/after/ftplugin/haskell.vim

and i am sure haskell-vim is loaded since i can use :HaskellAddModuleComment.

could you please have a look? it would be nice to let me know where the problem is.

`type role` syntax not supported

Example:

type role IO representational
{-
comments
-}

Comments are highlighted as code. Adding = ... to the type role line fixes it.

plugin clobbers my format option

I just spent an hour trying to find out why comments weren't automatically wrapping at text width, as I had set formatoptions+=t in my './config/nvim/init.vim'. Turns out this plug is changing my format options. While I don't see t in there explicitly, it certainly seems to be the case that for Haskell files this functionality breaks and :verbose set fo? tells me this plugin is the last thing to touch that option. Commenting out the offending line restores functionality.

As such, I'm wondering if there is a legitimate reason to set this from the plugin?

While I could add an after/ftplugin/haskell.vim file to my config, I'd prefer that my plugins didn't hijack my settings at all.

Guard indent not align

Code is Here:

import System.Environment
import System.IO
import System.IO.Error
import Control.Exception (catch)

main = toTry `catch` handler

toTry :: IO ()
toTry = do
  (fileName:_) <- getArgs
  contents <- readFile fileName
  putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"

handler :: IOError -> IO ()
handler e
  | isDoesNotExistError e = case ioeGetFileName e of 
                                 Just path -> putStrLn $ "Whoops! File does not exist at: " ++ path
                                 Nothing -> putStrLn "Whoops! File not exist at unknow location!"
                                   | otherwise = ioError e

'in' decreases indent as first characters of name

When using 'in' as the first two letters of a variable name (outside of a let/in expression) decreases indent without correcting when a third letter is typed.

To reproduce, try typing this.

f x = x'
    where x'  = x * 2
          ini = "something"

You will see typing the first two letters of the name 'ini' will cause the indent to be decreased (I assume it is assuming it is the keyword 'in' without correcting once the third letter is typed)

Where indentation doesn't seem to use configuration value

I like to indent the "where" keyword by 2 spaces from the expression it is contained within, and then indent the bindings in the where (on a newline) by 2 more spaces, so the bindings are indented 4 spaces from the expression containing them, thus:

foop = doop 1
  where 
    doop x = x + woop x
    woop x = 0

However, when I hit enter after doop 1 and type where, it correctly gets indented by 2 spaces (I don't think this is configurable, looking at the README?) but then after hitting enter and typing doop x and woop x, the indentation is as follows:

foop = doop 1
  where 
      doop x = x + woop x
      woop x = 0

I have set let g:haskell_indent_where = 2 in my .vimrc, but it doesn't appear to have any effect.

Alternative function definitions

So I have a very small feature request, namely that when I have very complicated functions, I prefer to write the function type as

function
    :: Classy x
    => x
    -> Foo
    -> Bar

haskell-vim however does not currently highlight 'function' as a function, but leaves it as plain text.

Indenting Record Syntax

The following is not properly indented:

data Customer = Customer
{ customerID      :: CustomerID
, customerName    :: String
, customerAddress :: Address
} deriving (Show)

The resulting code should look like this:

data Customer = Customer
    { customerID      :: CustomerID
    , customerName    :: String
    , customerAddress :: Address
    } deriving (Show)

Instead the {} part is not modified (or if it is already indented, the indentation is removed).

Error in let-in indentation

I think indentation of let expressions are handled wrong, it works like this:

let x =<CR>
    |

Cursor is shown as |. We need at least one more space here, otherwise it's a syntax error.

Haskell syntax colours break with primes

-- CASE 1
foo = bar'baz
-- Line not highlighted as comment
-- Line highlighted as comment
-- CASE 2
foo = bar'f'oo
-- 'f' is highlighted as a character literal

These two examples break the syntax colouring.

I've seen case 1 in actual code. I've never seen case 2, I just discovered it while investigating the cause of case 1.

I tried fixing this myself, changing the parts of the syntax file that assume primes can only be found at the end of a name. This did not fix case 1, and it obviously did not affect case 2.

Why are so many of the highlighting links arbitrary?

I really like the way indentation works with this, but I don't understand the syntax highlighting at all.

A large number of the highlighting links seem to be completely arbitrarily chosen.

It would be a lot more sensible to link syntax features to their semantic equivalents so that colors schemes will behave correctly and intuition about the meaning of colors will remain portable across languages.

Some examples that don't make any sense to me:

highlight def link haskellQuotedType Include
highlight def link haskellType Include
...
highlight def link haskellImportKeywords Structure
highlight def link haskellDeclKeyword Structure
highlight def link haskellDecl Structure
highlight def link haskellWhere Structure
highlight def link haskellLet Structure

Why aren't types linked to Type, normal keywords linked to Keyword and import keywords linked to Import?

I can always fork this and change things of course, but I'm wondering if there's some secret benefit to doing it this way that I've missed.

Strange indentation around braces

Suppose you prefer to lay data declarations out like:

data Foo
    = Foo
    { foo :: Int
    , bar :: Char
    , baz :: Int
    } deriving (Eq, Ord)

Attempting to input the above in a Haskell buffer with only haskell-vim loaded gives the following:

data Foo
    = Foo
        { foo :: Int
            , bar :: Char
            , baz :: Int
        } deriving (Eq, Ord)

The braces consistently align themselves, but the commas are always indented exactly one time.

Any idea how I might configure haskell-vim to not do that, or what part of the code might be causing that indentation?

Could function names be highlighted?

main :: IO () -- currently this line works well.
main = putStrLn "" -- "main" here is rendered as plain text (maybe, I am using solarized color theme).

Could "main" in the second line be colorful? Thanks.

Dedent return in do-block

.......................................................... $ do
        a <- ...
        b <- ...
        return
        |<--cursor here
|<-should be here

Customizable top-level definition indents

The way I indent top-level declarations is that I indent them with 4 spaces. Currently haskell-vim indents 2 spaces and it's not customizable. Adding this would be great.

Example:

topLevelDecl =<CR>
  |

| is the cursor.

CPP macros aren't highlighted in some cases

For example, if it follows directly after an import statement, it's not highlighted properly:

import Control.Monad
#include "Blah.h"

but this works:

import Control.Monad
f = blah
#include "Blah.h"

.hsc is a Haskell file format

First of all, thanks for this awesome work. I just switched from vim2hs and this is way better.

I'm working on .hsc files a lot, vim2hs was considering those files as Haskell files and enabling ft automatically. Adding same thing to haskell-vim would be nice.

(I currently have something like au! BufRead,BufNewFile *.hsc set filetype=haskell in my .vimrc)

String escapes mess up the highlighting

Example:

putStrLn "test \"substring\" test" -- haskell-vim continues to highlight as if the string never closed

haskell-vim correctly ignores the first escape, but treats the second as string terminating. What's also wrong is that by default string highlighting continues even after a newline, while this should be the case only with a multiline string.

It appears that escaped double quotes are misinterpreted only when they are preceded by a letter. In the example above, only the second one is preceded by a letter and interpreted as a string-terminating quote.

some valid identifiers are not highlighted

Here are some examples:

lit_1'Integer
subtract'Integer
exp1 `add'Rational` exp2

The part until ' is not highlighted at all, second part is highlighted as if it's a constructor. If second part doesn't start with upper case letter, then it's not highlighted. (example: blah'blah)

In addition, third one is not highlighted as an infix operator.

Not correct highlighting

Hi there, i just installed haskell-vim plugin hoping for better syntax with haskell source files, however highlighting is not working as it should:

gruvbox
solarized

I've already uninstalled and re-installed the plugin but it does not seem to work.
I also tried removing the colorscheme, and using Neovim's defaut, however without sucess
I'm using neovim 0.1.3 in a Arch Linux machine

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.