Sublime Forum

ST 2/3 Haskell highlighting: various fixes inside

#1

Haskell highlighting in Sublime 2/3 is not very good, so I just went to fix some of the stuff. You can find a file with some pathological examples at the end of this post, just paste it in Sublime and see what it looks like. Note that there are some of the class “it’s a bug unless you can disable it” in in the list, just ignore those if you like them. A very brief description on how to fix the bug is also included, just edit your Haskell.tmLanguage file accordingly.

Fixed:

  • Comments in module declarations

  • Comments in imports

  • “import” or “module” inside an identifier doesn’t break the whole layout anymore

  • Multiline strings aren’t allowed in Haskell, but Sublime highlighted them as if they were (C-style)

Not fixed yet:

  • Multiple type constraints for one function

  • Multiline type declarations

Ideas on how to fix the remaining bugs are welcome.

[code]-- Sublime text Haskell highlighting issues

– Comments in module declarations don’t render properly
– Fix: Add the following after the dict block containing “I don’t know”

– include
#comments

module Main (
foo, – This doesn’t render as a comment
bar
) where – Works

– Comments after imports don’t work
– Fix: Add the following after the #module_exports dict element:

– include
#comments

import Data.List – Comment
import Data.Maybe {- Comment -}

– Class constraints are highlighted differently when they’re parenthesized
– Fix: Change
– (\s*([A-Z][A-Za-z])\s+([a-z][A-Za-z_’]))\s*(=>)
– to
– (?x) # x turns off whitespaces. This block matches a single constraint. Fix?
– (\s*[A-Z][A-Za-z])\s+([a-z][A-Za-z_’])\s*(=>) # Non-parenthesized expression
– |
– (\s*([A-Z][A-Za-z])\s+([a-z][A-Za-z_’]))\s*(=>) # Parenthesized expression

cc :: (Monad a) => a -> a
cc’ :: Monad a => a -> a

– Custom types in type declarations have the wrong color
bar :: MyType a -> String -> String

– Multiline type declarations render wrong
foo :: (Eq a) => MyType a -> String
-> String

– ‘module’ or ‘import’ in an identifier breaks highlighting.
– Fix: change “(module)” to “\b(module)\b”
moduleInName = “This should render as a string, but doesn’t.\n”
importInName = “This should render as a string, but doesn’t.\n”

– Strings over multiple lines have to be explicitly constructed using (++).
– Fix: Go to the string section and change
– end
– "
– to
– end
– $|"
baz = “You cannot write multiline
strings directly in Haskell,
but Sublime colors them like it.”

– Prelude names get special highlighting
– Fix: Comment out the dict block containing ‘length’ and ‘IO(Error)?’
qux :: IO Int -> Maybe Int
qux = length [1…10][/code]

0 Likes

#2

Thanks a ton for this!! I was considering fixing this, because I need to work on a module that has a lot of uses of “module”.

Probably would have been better to provide a diff… Finding where to do those changes wasn’t so trivial. Otherwise, great work! Some bugs I didn’t know were there.

I fixed the multiline types by ending them on things that look like top level declarations, and fixed the constraints by removing special casing - constraints are near enough to types these days anyway. I hope some of these fixes make it to the main highlighter!

Finally, I added support for highlighting quasi-quotes as if they were multiline strings, and made $( parse as a keyword (even if the ending paren isn’t opreatorized…)

---Haskell.tmLanguageOld
+++ Haskell.tmLanguage
@@ -47,7 +47,7 @@
 		</dict>
 		<dict>
 			<key>begin</key>
-			<string>(module)</string>
+			<string>\b(module)\b</string>
 			<key>beginCaptures</key>
 			<dict>
 				<key>1</key>
@@ -193,6 +193,10 @@
 					<key>include</key>
 					<string>#module_exports</string>
 				</dict>
+				<dict>
+					<key>include</key>
+					<string>#comments</string>
+				</dict>
 			</array>
 		</dict>
 		<dict>
@@ -284,7 +288,7 @@
 				</dict>
 			</dict>
 			<key>end</key>
-			<string>"</string>
+			<string>$|"</string>
 			<key>endCaptures</key>
 			<dict>
 				<key>0</key>
@@ -314,6 +318,104 @@
 					<string>\^[A-Z@\[\]\\\^_]</string>
 					<key>name</key>
 					<string>constant.character.escape.control.haskell</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>comment</key>
+			<dict>Points out splices in ast quotes</dict>
+			<key>begin</key>
+			<string>\(?:|e|d|t|p)\|</string>
+			<key>beginCaptures</key>
+			<dict>
+				<key>0</key>
+				<dict>
+					<key>name</key>
+					<string>keyword.other.quasibracket.haskell</string>
+				</dict>
+			</dict>
+			<key>end</key>
+			<string>(.*)(\|\])</string>
+			<key>endCaptures</key>
+			<dict>
+				<key>1</key>
+				<dict>
+					<key>name</key>
+					<string>string.quasiquoted.haskell</string>
+				</dict>
+				<key>2</key>
+				<dict>
+					<key>name</key>
+					<string>keyword.other.quasibracket.haskell</string>
+				</dict>
+			</dict>
+			<key>name</key>
+			<string>meta.other.quasiquote.haskell</string>
+			<key>patterns</key>
+			<array>
+				<dict>
+					<key>match</key>
+					<string>\$\(</string>
+					<key>name</key>
+					<string>keyword.other.splice.haskell</string>
+				</dict>
+				<dict>
+					<key>match</key>
+					<string>\$</string>
+					<key>name</key>
+					<string>string.quasiquoted.haskell</string>
+				</dict>
+				<dict>
+					<key>match</key>
+					<string>^$]*</string>
+					<key>name</key>
+					<string>string.quasiquoted.haskell</string>
+				</dict>
+			</array>
+		</dict>
+		<dict>
+			<key>comment</key>
+			<string>Highlight the beginning of a splice.</string>
+			<key>match</key>
+			<string>\$\(</string>
+			<key>name</key>
+			<string>keyword.other.splice.haskell</string>
+		</dict>
+		<dict>
+			<key>begin</key>
+			<string>\[a-zA-Z0-9_']*\|</string>
+			<key>beginCaptures</key>
+			<dict>
+				<key>0</key>
+				<dict>
+					<key>name</key>
+					<string>keyword.other.quasibracket.haskell</string>
+				</dict>
+			</dict>
+			<key>end</key>
+			<string>(.*)(\|\])</string>
+			<key>endCaptures</key>
+			<dict>
+				<key>1</key>
+				<dict>
+					<key>name</key>
+					<string>string.quasiquoted.haskell</string>
+				</dict>
+				<key>2</key>
+				<dict>
+					<key>name</key>
+					<string>keyword.other.quasibracket.haskell</string>
+				</dict>
+			</dict>
+			<key>name</key>
+			<string>meta.other.quasiquote.haskell</string>
+			<key>patterns</key>
+			<array>
+				<dict>
+					<key>match</key>
+					<string>.*</string>
+					<key>name</key>
+					<string>string.quasiquoted.haskell</string>
 				</dict>
 			</array>
 		</dict>
@@ -385,7 +487,23 @@
 				</dict>
 			</dict>
 			<key>end</key>
-			<string>$\n?</string>
+			<string>(?x)
+				  ^(data|newtype|type|class|deriving)\s  # When a top level declaration starts
+				| ^^=]*(=)\sa-zA-Z0-9_\(]                # A function declaration
+			</string>
+			<key>endCaptures</key>
+			<dict>
+				<key>1</key>
+				<dict>
+					<key>name</key>
+					<string>keyword.other.haskell</string>
+				</dict>
+				<key>2</key>
+				<dict>
+					<key>name</key>
+					<string>keyword.operator.haskell</string>
+				</dict>
+			</dict>
 			<key>name</key>
 			<string>meta.function.type-declaration.haskell</string>
 			<key>patterns</key>
@@ -395,12 +513,6 @@
 					<string>#type_signature</string>
 				</dict>
 			</array>
-		</dict>
-		<dict>
-			<key>match</key>
-			<string>\b(Just|Nothing|Left|Right|True|False|LT|EQ|GT|\(\)|\\])\b</string>
-			<key>name</key>
-			<string>support.constant.haskell</string>
 		</dict>
 		<dict>
 			<key>match</key>
@@ -411,12 +523,6 @@
 		<dict>
 			<key>include</key>
 			<string>#comments</string>
-		</dict>
-		<dict>
-			<key>match</key>
-			<string>\b(abs|acos|acosh|all|and|any|appendFile|applyM|asTypeOf|asin|asinh|atan|atan2|atanh|break|catch|ceiling|compare|concat|concatMap|const|cos|cosh|curry|cycle|decodeFloat|div|divMod|drop|dropWhile|elem|encodeFloat|enumFrom|enumFromThen|enumFromThenTo|enumFromTo|error|even|exp|exponent|fail|filter|flip|floatDigits|floatRadix|floatRange|floor|fmap|foldl|foldl1|foldr|foldr1|fromEnum|fromInteger|fromIntegral|fromRational|fst|gcd|getChar|getContents|getLine|head|id|init|interact|ioError|isDenormalized|isIEEE|isInfinite|isNaN|isNegativeZero|iterate|last|lcm|length|lex|lines|log|logBase|lookup|map|mapM|mapM_|max|maxBound|maximum|maybe|min|minBound|minimum|mod|negate|not|notElem|null|odd|or|otherwise|pi|pred|print|product|properFraction|putChar|putStr|putStrLn|quot|quotRem|read|readFile|readIO|readList|readLn|readParen|reads|readsPrec|realToFrac|recip|rem|repeat|replicate|return|reverse|round|scaleFloat|scanl|scanl1|scanr|scanr1|seq|sequence|sequence_|show|showChar|showList|showParen|showString|shows|showsPrec|significand|signum|sin|sinh|snd|span|splitAt|sqrt|subtract|succ|sum|tail|take|takeWhile|tan|tanh|toEnum|toInteger|toRational|truncate|uncurry|undefined|unlines|until|unwords|unzip|unzip3|userError|words|writeFile|zip|zip3|zipWith|zipWith3)\b</string>
-			<key>name</key>
-			<string>support.function.prelude.haskell</string>
 		</dict>
 		<dict>
 			<key>include</key>
@@ -536,6 +642,10 @@
 					<key>name</key>
 					<string>meta.other.unknown.haskell</string>
 				</dict>
+				<dict>
+					<key>include</key>
+					<string>#comments</string>
+				</dict>
 			</array>
 		</dict>
 		<key>module_name</key>
@@ -568,30 +678,6 @@
 			<key>patterns</key>
 			<array>
 				<dict>
-					<key>captures</key>
-					<dict>
-						<key>1</key>
-						<dict>
-							<key>name</key>
-							<string>entity.other.inherited-class.haskell</string>
-						</dict>
-						<key>2</key>
-						<dict>
-							<key>name</key>
-							<string>variable.other.generic-type.haskell</string>
-						</dict>
-						<key>3</key>
-						<dict>
-							<key>name</key>
-							<string>keyword.other.big-arrow.haskell</string>
-						</dict>
-					</dict>
-					<key>match</key>
-					<string>\(\s*([A-Z][A-Za-z]*)\s+([a-z][A-Za-z_']*)\)\s*(=&gt;)</string>
-					<key>name</key>
-					<string>meta.class-constraint.haskell</string>
-				</dict>
-				<dict>
 					<key>include</key>
 					<string>#pragma</string>
 				</dict>
@@ -606,12 +692,6 @@
 					<string>=&gt;</string>
 					<key>name</key>
 					<string>keyword.other.big-arrow.haskell</string>
-				</dict>
-				<dict>
-					<key>match</key>
-					<string>\b(Int(eger)?|Maybe|Either|Bool|Float|Double|Char|String|Ordering|ShowS|ReadS|FilePath|IO(Error)?)\b</string>
-					<key>name</key>
-					<string>support.type.prelude.haskell</string>
 				</dict>
 				<dict>
 					<key>match</key>
0 Likes

#3

Updated the above post with a better regex for the end of type signatures, before it was broken for indented type signatures. It’s still not perfect:

  • Mulitple type declarations in a row (like in typeclasses) won’t have the name of the function / (::slight_smile: highlighted specially in the declarations that come after the first

  • Highlighting of pattern matching broken immediately after type declaration

  • Type declarations where (::slight_smile: comes on the next line (a reasonably common style) don’t highlight the type.

0 Likes

#4

Hi there,

I couldn’t reproduce any of those highlighting errors, but I found some other ones:

-- This is highlighted correctly:

function0 :: String -> String	-- function type declaration
function0 = id					-- function implementation

-- Syntax highlighting fails here...

class MyClass x where

	function1 :: x -> x		-- correctly highlighted
	function2 :: x -> x		-- is a function type declaration (should be colored like function 1)
	function3 :: x -> x		-- is a function type declaration (should be colored like function 1)
	function4 :: x -> x		-- is a function type declaration (should be colored like function 1)
	function4 = id			-- is colored corectly, as it is a defaultimplementation of function4
	function5 :: x -> x		-- is colored corectly
	function6 :: x -> x		-- is a function type declaration (should be colored like function 1)

-- ... and here

function7 :: String -> String
function7 = id

Scrrenshot of it:
http://imgur.com/Xb5Hofz

What should I do about it?

0 Likes

#5

I’m also wondering how to fix some syntax highlighting errors.

Function name should be orange but turns out green in the data constructor definition. (:: isn’t blue too)
Also, the function defined under the data Example doesn’t have the orange highlighting(if I define another function then everything is fine).

0 Likes