diff --git a/crates/zed/src/languages/haskell/config.toml b/crates/zed/src/languages/haskell/config.toml index 15fdd7b78e..9d0c3f602c 100644 --- a/crates/zed/src/languages/haskell/config.toml +++ b/crates/zed/src/languages/haskell/config.toml @@ -7,6 +7,7 @@ brackets = [ { start = "{", end = "}", close = true, newline = true }, { start = "[", end = "]", close = true, newline = true }, { start = "(", end = ")", close = true, newline = true }, - { start = "\"", end = "\"", close = false, newline = false }, - { start = "'", end = "'", close = false, newline = false }, + { start = "\"", end = "\"", close = true, newline = false }, + { start = "'", end = "'", close = true, newline = false }, + { start = "`", end = "`", close = true, newline = false }, ] diff --git a/crates/zed/src/languages/haskell/folds.scm b/crates/zed/src/languages/haskell/folds.scm new file mode 100644 index 0000000000..e743ecf52c --- /dev/null +++ b/crates/zed/src/languages/haskell/folds.scm @@ -0,0 +1,18 @@ +;; Copyright 2022 nvim-treesitter +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +[ + (exp_apply) + (exp_do) + (function) +] @fold diff --git a/crates/zed/src/languages/haskell/highlights.scm b/crates/zed/src/languages/haskell/highlights.scm index 3ab843e833..a81089d419 100644 --- a/crates/zed/src/languages/haskell/highlights.scm +++ b/crates/zed/src/languages/haskell/highlights.scm @@ -11,24 +11,77 @@ ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;; See the License for the specific language governing permissions and ;; limitations under the License. +; ---------------------------------------------------------------------------- +; Parameters and variables +; NOTE: These are at the top, so that they have low priority, +; and don't override destructured parameters +(variable) @variable -;; ---------------------------------------------------------------------------- -;; Literals and comments +(pat_wildcard) @variable +(function + patterns: + (patterns + (_) @variable.parameter)) + +(exp_lambda + (_)+ @variable.parameter + "->") + +(function + infix: + (infix + lhs: (_) @variable.parameter)) + +(function + infix: + (infix + rhs: (_) @variable.parameter)) + +; ---------------------------------------------------------------------------- +; Literals and comments (integer) @number + (exp_negation) @number -(exp_literal (float)) @float + +(exp_literal + (float)) @number.float + (char) @character + (string) @string -(con_unit) @symbol ; unit, as in () +(con_unit) @string.special.symbol ; unit, as in () (comment) @comment - -;; ---------------------------------------------------------------------------- -;; Punctuation - +; FIXME: The below documentation comment queries are inefficient +; and need to be anchored, using something like +; ((comment) @_first . (comment)+ @comment.documentation) +; once https://github.com/neovim/neovim/pull/24738 has been merged. +; +; ((comment) @comment.documentation +; (#lua-match? @comment.documentation "^-- |")) +; +; ((comment) @_first @comment.documentation +; (comment) @comment.documentation +; (#lua-match? @_first "^-- |")) +; +; ((comment) @comment.documentation +; (#lua-match? @comment.documentation "^-- %^")) +; +; ((comment) @_first @comment.documentation +; (comment) @comment.documentation +; (#lua-match? @_first "^-- %^")) +; +; ((comment) @comment.documentation +; (#lua-match? @comment.documentation "^{-")) +; +; ((comment) @_first @comment.documentation +; (comment) @comment.documentation +; (#lua-match? @_first "^{-")) +; ---------------------------------------------------------------------------- +; Punctuation [ "(" ")" @@ -43,16 +96,14 @@ ";" ] @punctuation.delimiter - -;; ---------------------------------------------------------------------------- -;; Keywords, operators, includes - +; ---------------------------------------------------------------------------- +; Keywords, operators, includes [ "forall" "∀" -] @repeat +] @keyword.repeat -(pragma) @constant.macro +(pragma) @keyword.directive [ "if" @@ -60,24 +111,26 @@ "else" "case" "of" -] @conditional - -(exp_lambda_cases "\\" ("cases" @conditional)) +] @keyword.conditional [ "import" "qualified" "module" -] @include +] @keyword.import [ (operator) (constructor_operator) (type_operator) (tycon_arrow) - (qualified_module) ; grabs the `.` (dot), ex: import System.IO + (qualified_module) ; grabs the `.` (dot), ex: import System.IO + (qualified_type) + (qualified_variable) (all_names) (wildcard) + "." + ".." "=" "|" "::" @@ -89,7 +142,26 @@ "@" ] @operator -(module) @namespace +(module) @module + +((qualified_module + (module) @constructor) + . + (module)) + +(qualified_type + (module) @module) + +(qualified_variable + (module) @module) + +(import + (module) @module) + +(import + (module) @constructor + . + (module)) [ (where) @@ -97,6 +169,7 @@ "in" "class" "instance" + "pattern" "data" "newtype" "family" @@ -115,42 +188,445 @@ "infixr" ] @keyword +; ---------------------------------------------------------------------------- +; Functions and variables +(signature + name: (variable) @function) -;; ---------------------------------------------------------------------------- -;; Functions and variables +(function + name: (variable) @function) -(variable) @variable -(pat_wildcard) @variable +(function + name: (variable) @variable + rhs: + [ + (exp_literal) + (exp_apply + (exp_name + [ + (constructor) + (variable) + (qualified_variable) + ])) + (quasiquote) + ((exp_name) + . + (operator)) + ]) -(signature name: (variable) @type) +(function + name: (variable) @variable + rhs: + (exp_infix + [ + (exp_literal) + (exp_apply + (exp_name + [ + (constructor) + (variable) + (qualified_variable) + ])) + (quasiquote) + ((exp_name) + . + (operator)) + ])) + +; Consider signatures (and accompanying functions) +; with only one value on the rhs as variables +(signature + . + (variable) @variable + . + (_) .) + +((signature + . + (variable) @_name + . + (_) .) + . + (function + name: (variable) @variable) + (#eq? @_name @variable)) + +; but consider a type that involves 'IO' a function +(signature + name: (variable) @function + . + (type_apply + (type_name) @_type) + (#eq? @_type "IO")) + +((signature + name: (variable) @_name + . + (type_apply + (type_name) @_type) + (#eq? @_type "IO")) + . + (function + name: (variable) @function) + (#eq? @_name @function)) + +; functions with parameters +; + accompanying signatures (function name: (variable) @function patterns: (patterns)) -((signature (fun)) . (function (variable) @function)) -((signature (context (fun))) . (function (variable) @function)) -((signature (forall (context (fun)))) . (function (variable) @function)) -(exp_infix (variable) @operator) ; consider infix functions as operators +((signature) @function + . + (function + name: (variable) @function + patterns: (patterns))) -(exp_infix (exp_name) @function (#set! "priority" 101)) -(exp_apply . (exp_name (variable) @function)) -(exp_apply . (exp_name (qualified_variable (variable) @function))) +(function + name: (variable) @function + rhs: (exp_lambda)) +; view patterns +(pat_view + (exp_name + [ + (variable) @function.call + (qualified_variable + (variable) @function.call) + ])) -;; ---------------------------------------------------------------------------- -;; Types +; consider infix functions as operators +(exp_infix + [ + (variable) @operator + (qualified_variable + (variable) @operator) + ]) +; partially applied infix functions (sections) also get highlighted as operators +(exp_section_right + [ + (variable) @operator + (qualified_variable + (variable) @operator) + ]) + +(exp_section_left + [ + (variable) @operator + (qualified_variable + (variable) @operator) + ]) + +; function calls with an infix operator +; e.g. func <$> a <*> b +(exp_infix + (exp_name + [ + (variable) @function.call + (qualified_variable + ((module) @module + (variable) @function.call)) + ]) + . + (operator)) + +; infix operators applied to variables +((exp_name + (variable) @variable) + . + (operator)) + +((operator) + . + (exp_name + [ + (variable) @variable + (qualified_variable + (variable) @variable) + ])) + +; function calls with infix operators +((exp_name + [ + (variable) @function.call + (qualified_variable + (variable) @function.call) + ]) + . + (operator) @_op + (#any-of? @_op "$" "<$>" ">>=" "=<<")) + +; right hand side of infix operator +((exp_infix + [ + (operator) + (variable) + ] ; infix or `func` + . + (exp_name + [ + (variable) @function.call + (qualified_variable + (variable) @function.call) + ])) + . + (operator) @_op + (#any-of? @_op "$" "<$>" "=<<")) + +; function composition, arrows, monadic composition (lhs) +((exp_name + [ + (variable) @function + (qualified_variable + (variable) @function) + ]) + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; right hand side of infix operator +((exp_infix + [ + (operator) + (variable) + ] ; infix or `func` + . + (exp_name + [ + (variable) @function + (qualified_variable + (variable) @function) + ])) + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function composition, arrows, monadic composition (rhs) +((operator) @_op + . + (exp_name + [ + (variable) @function + (qualified_variable + (variable) @function) + ]) + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function defined in terms of a function composition +(function + name: (variable) @function + rhs: + (exp_infix + (_) + . + (operator) @_op + . + (_) + (#any-of? @_op "." ">>>" "***" ">=>" "<=<"))) + +(exp_apply + (exp_name + [ + (variable) @function.call + (qualified_variable + (variable) @function.call) + ])) + +; function compositions, in parentheses, applied +; lhs +(exp_apply + . + (exp_parens + (exp_infix + (exp_name + [ + (variable) @function.call + (qualified_variable + (variable) @function.call) + ]) + . + (operator)))) + +; rhs +(exp_apply + . + (exp_parens + (exp_infix + (operator) + . + (exp_name + [ + (variable) @function.call + (qualified_variable + (variable) @function.call) + ])))) + +; variables being passed to a function call +(exp_apply + (_)+ + . + (exp_name + [ + (variable) @variable + (qualified_variable + (variable) @variable) + ])) + +; Consider functions with only one value on the rhs +; as variables, e.g. x = Rec {} or x = foo +(function + . + (variable) @variable + . + [ + (exp_record) + (exp_name + [ + (variable) + (qualified_variable) + ]) + (exp_list) + (exp_tuple) + (exp_cond) + ] .) + +; main is always a function +; (this prevents `main = undefined` from being highlighted as a variable) +(function + name: (variable) @function + (#eq? @function "main")) + +; scoped function types (func :: a -> b) +(pat_typed + pattern: + (pat_name + (variable) @function) + type: (fun)) + +; signatures that have a function type +; + functions that follow them +(signature + (variable) @function + (fun)) + +((signature + (variable) @_type + (fun)) + . + (function + (variable) @function) + (#eq? @function @_type)) + +(signature + (variable) @function + (context + (fun))) + +((signature + (variable) @_type + (context + (fun))) + . + (function + (variable) @function) + (#eq? @function @_type)) + +((signature + (variable) @function + (forall + (context + (fun)))) + . + (function + (variable))) + +((signature + (variable) @_type + (forall + (context + (fun)))) + . + (function + (variable) @function) + (#eq? @function @_type)) + +; ---------------------------------------------------------------------------- +; Types (type) @type + +(type_star) @type + (type_variable) @type (constructor) @constructor ; True or False -((constructor) @_bool (#match? @_bool "(True|False)")) @boolean +((constructor) @boolean + (#any-of? @boolean "True" "False")) +; otherwise (= True) +((variable) @boolean + (#eq? @boolean "otherwise")) -;; ---------------------------------------------------------------------------- -;; Quasi-quotes +; ---------------------------------------------------------------------------- +; Quasi-quotes +(quoter) @function.call -(quoter) @function -; Highlighting of quasiquote_body is handled by injections.scm +(quasiquote + [ + (quoter) @_name + (_ + (variable) @_name) + ] + (#eq? @_name "qq") + (quasiquote_body) @string) + +(quasiquote + (_ + (variable) @_name) + (#eq? @_name "qq") + (quasiquote_body) @string) + +; namespaced quasi-quoter +(quasiquote + (_ + (module) @module + . + (variable) @function.call)) + +; Highlighting of quasiquote_body for other languages is handled by injections.scm +; ---------------------------------------------------------------------------- +; Exceptions/error handling +((variable) @keyword.exception + (#any-of? @keyword.exception "error" "undefined" "try" "tryJust" "tryAny" "catch" "catches" "catchJust" "handle" "handleJust" "throw" "throwIO" "throwTo" "throwError" "ioError" "mask" "mask_" "uninterruptibleMask" "uninterruptibleMask_" "bracket" "bracket_" "bracketOnErrorSource" "finally" "fail" "onException" "expectationFailure")) + +; ---------------------------------------------------------------------------- +; Debugging +((variable) @keyword.debug + (#any-of? @keyword.debug "trace" "traceId" "traceShow" "traceShowId" "traceWith" "traceShowWith" "traceStack" "traceIO" "traceM" "traceShowM" "traceEvent" "traceEventWith" "traceEventIO" "flushEventLog" "traceMarker" "traceMarkerIO")) + +; ---------------------------------------------------------------------------- +; Fields +(field + (variable) @variable.member) + +(pat_field + (variable) @variable.member) + +(exp_projection + field: (variable) @variable.member) + +(import_item + (type) + . + (import_con_names + (variable) @variable.member)) + +(exp_field + field: + [ + (variable) @variable.member + (qualified_variable + (variable) @variable.member) + ]) diff --git a/crates/zed/src/languages/haskell/injections.scm b/crates/zed/src/languages/haskell/injections.scm new file mode 100644 index 0000000000..237dbfa0ee --- /dev/null +++ b/crates/zed/src/languages/haskell/injections.scm @@ -0,0 +1,89 @@ +;; Copyright 2022 nvim-treesitter +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +; ----------------------------------------------------------------------------- +; General language injection +(quasiquote + (quoter) @injection.language + (quasiquote_body) @injection.content) + +((comment) @injection.content + (#set! injection.language "comment")) + +; ----------------------------------------------------------------------------- +; shakespeare library +; NOTE: doesn't support templating +; TODO: add once CoffeeScript parser is added +; ; CoffeeScript: Text.Coffee +; (quasiquote +; (quoter) @_name +; (#eq? @_name "coffee") +; ((quasiquote_body) @injection.content +; (#set! injection.language "coffeescript"))) +; CSS: Text.Cassius, Text.Lucius +(quasiquote + (quoter) @_name + (#any-of? @_name "cassius" "lucius") + (quasiquote_body) @injection.content + (#set! injection.language "css")) + +; HTML: Text.Hamlet +(quasiquote + (quoter) @_name + (#any-of? @_name "shamlet" "xshamlet" "hamlet" "xhamlet" "ihamlet") + (quasiquote_body) @injection.content + (#set! injection.language "html")) + +; JS: Text.Julius +(quasiquote + (quoter) @_name + (#any-of? @_name "js" "julius") + (quasiquote_body) @injection.content + (#set! injection.language "javascript")) + +; TS: Text.TypeScript +(quasiquote + (quoter) @_name + (#any-of? @_name "tsc" "tscJSX") + (quasiquote_body) @injection.content + (#set! injection.language "typescript")) + +; ----------------------------------------------------------------------------- +; HSX +(quasiquote + (quoter) @_name + (#eq? @_name "hsx") + (quasiquote_body) @injection.content + (#set! injection.language "html")) + +; ----------------------------------------------------------------------------- +; Inline JSON from aeson +(quasiquote + (quoter) @_name + (#eq? @_name "aesonQQ") + (quasiquote_body) @injection.content + (#set! injection.language "json")) + +; ----------------------------------------------------------------------------- +; SQL +; postgresql-simple +(quasiquote + (quoter) @injection.language + (#eq? @injection.language "sql") + (quasiquote_body) @injection.content) + +(quasiquote + (quoter) @_name + (#any-of? @_name "persistUpperCase" "persistLowerCase" "persistWith") + (quasiquote_body) @injection.content + (#set! injection.language "haskell_persistent"))