diff --git a/Cargo.lock b/Cargo.lock index 3731f19aab3136d0f270a5118c736bbb0681c0b8..1f54ef9974947f7bf164d1cfa3752a27ced76e6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8457,6 +8457,15 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-haskell" +version = "0.14.0" +source = "git+https://github.com/tree-sitter/tree-sitter-haskell?rev=dd924b8df1eb76261f009e149fc6f3291c5081c2#dd924b8df1eb76261f009e149fc6f3291c5081c2" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-heex" version = "0.0.1" @@ -9722,6 +9731,7 @@ dependencies = [ "tree-sitter-gleam", "tree-sitter-glsl", "tree-sitter-go", + "tree-sitter-haskell", "tree-sitter-heex", "tree-sitter-html", "tree-sitter-json 0.20.0", diff --git a/Cargo.toml b/Cargo.toml index bad9e149bc50d96abc32b000d4002665f7545685..e01f24f727f29605dcd30511196058458363da6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -151,6 +151,7 @@ tree-sitter-python = "0.20.2" tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" } tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" } tree-sitter-ruby = "0.20.0" +tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "dd924b8df1eb76261f009e149fc6f3291c5081c2" } tree-sitter-html = "0.19.0" tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"} tree-sitter-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "697bb515471871e85ff799ea57a76298a71a9cca"} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index a4084a7bd9437b14502843e61c7cff8ccf3a8326..d14c59e54fdb2fd99de639d1e74c79d8d3cf109f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -131,6 +131,7 @@ tree-sitter-python.workspace = true tree-sitter-toml.workspace = true tree-sitter-typescript.workspace = true tree-sitter-ruby.workspace = true +tree-sitter-haskell.workspace = true tree-sitter-html.workspace = true tree-sitter-php.workspace = true tree-sitter-scheme.workspace = true diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index cb557ced8dd1eee42d24ba6eb306d048587b8bc8..0e14c4ab9a6cf432f260760385b3881150ee429b 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -15,6 +15,7 @@ mod deno; mod elixir; mod gleam; mod go; +mod haskell; mod html; mod json; #[cfg(feature = "plugin_runtime")] @@ -201,6 +202,12 @@ pub fn init( ); } } + + language( + "haskell", + tree_sitter_haskell::language(), + vec![Arc::new(haskell::HaskellLanguageServer {})], + ); language( "html", tree_sitter_html::language(), diff --git a/crates/zed/src/languages/haskell.rs b/crates/zed/src/languages/haskell.rs new file mode 100644 index 0000000000000000000000000000000000000000..6667267702d23239beb27b79717d3fb7db8b76ee --- /dev/null +++ b/crates/zed/src/languages/haskell.rs @@ -0,0 +1,55 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use lsp::LanguageServerBinary; +use std::{any::Any, path::PathBuf}; + +pub struct HaskellLanguageServer; + +#[async_trait] +impl LspAdapter for HaskellLanguageServer { + fn name(&self) -> LanguageServerName { + LanguageServerName("hls".into()) + } + + fn short_name(&self) -> &'static str { + "hls" + } + + async fn fetch_latest_server_version( + &self, + _: &dyn LspAdapterDelegate, + ) -> Result> { + Ok(Box::new(())) + } + + async fn fetch_server_binary( + &self, + _version: Box, + _container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + Err(anyhow!( + "hls (haskell language server) must be installed via ghcup" + )) + } + + async fn cached_server_binary( + &self, + _: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + Some(LanguageServerBinary { + path: "haskell-language-server-wrapper".into(), + arguments: vec!["lsp".into()], + }) + } + + fn can_be_reinstalled(&self) -> bool { + false + } + + async fn installation_test_binary(&self, _: PathBuf) -> Option { + None + } +} diff --git a/crates/zed/src/languages/haskell/brackets.scm b/crates/zed/src/languages/haskell/brackets.scm new file mode 100644 index 0000000000000000000000000000000000000000..191fd9c084a52eced37428281971ff9e569a4932 --- /dev/null +++ b/crates/zed/src/languages/haskell/brackets.scm @@ -0,0 +1,3 @@ +("(" @open ")" @close) +("[" @open "]" @close) +("{" @open "}" @close) diff --git a/crates/zed/src/languages/haskell/config.toml b/crates/zed/src/languages/haskell/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..9d0c3f602ce9e2985ae831302b1aa08ae616cccd --- /dev/null +++ b/crates/zed/src/languages/haskell/config.toml @@ -0,0 +1,13 @@ +name = "Haskell" +path_suffixes = ["hs"] +autoclose_before = ",=)}]" +line_comment = "-- " +block_comment = ["{- ", " -}"] +brackets = [ + { start = "{", end = "}", close = true, newline = true }, + { start = "[", end = "]", close = true, newline = true }, + { start = "(", end = ")", close = true, newline = true }, + { 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/highlights.scm b/crates/zed/src/languages/haskell/highlights.scm new file mode 100644 index 0000000000000000000000000000000000000000..aca744f5cd50d6da088f0d647e9c3020dcbbe9b7 --- /dev/null +++ b/crates/zed/src/languages/haskell/highlights.scm @@ -0,0 +1,156 @@ +;; 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. + +;; ---------------------------------------------------------------------------- +;; Literals and comments + +(integer) @number +(exp_negation) @number +(exp_literal (float)) @float +(char) @character +(string) @string + +(con_unit) @symbol ; unit, as in () + +(comment) @comment + + +;; ---------------------------------------------------------------------------- +;; Punctuation + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + (comma) + ";" +] @punctuation.delimiter + + +;; ---------------------------------------------------------------------------- +;; Keywords, operators, includes + +[ + "forall" + "∀" +] @keyword + +(pragma) @constant + +[ + "if" + "then" + "else" + "case" + "of" +] @keyword + +(exp_lambda_cases "\\" ("cases" @variant)) + +[ + "import" + "qualified" + "module" +] @keyword + +[ + (operator) + (constructor_operator) + (type_operator) + (tycon_arrow) + (qualified_module) ; grabs the `.` (dot), ex: import System.IO + (all_names) + (wildcard) + "=" + "|" + "::" + "=>" + "->" + "<-" + "\\" + "`" + "@" +] @operator + +(module) @title + +[ + (where) + "let" + "in" + "class" + "instance" + "data" + "newtype" + "family" + "type" + "as" + "hiding" + "deriving" + "via" + "stock" + "anyclass" + "do" + "mdo" + "rec" + "infix" + "infixl" + "infixr" +] @keyword + + +;; ---------------------------------------------------------------------------- +;; Functions and variables + +(variable) @variable +(pat_wildcard) @variable + +(signature name: (variable) @type) +(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 + +(exp_infix (exp_name) @function (#set! "priority" 101)) +(exp_apply . (exp_name (variable) @function)) +(exp_apply . (exp_name (qualified_variable (variable) @function))) + + +;; ---------------------------------------------------------------------------- +;; Types + +(type) @type +(type_variable) @type + +(constructor) @constructor + +; True or False +((constructor) @_bool (#match? @_bool "(True|False)")) @boolean + + +;; ---------------------------------------------------------------------------- +;; Quasi-quotes + +(quoter) @function +; Highlighting of quasiquote_body is handled by injections.scm diff --git a/crates/zed/src/languages/haskell/indents.scm b/crates/zed/src/languages/haskell/indents.scm new file mode 100644 index 0000000000000000000000000000000000000000..112b414aa45f277138d0c681851129a608ee96e0 --- /dev/null +++ b/crates/zed/src/languages/haskell/indents.scm @@ -0,0 +1,3 @@ +(_ "[" "]" @end) @indent +(_ "{" "}" @end) @indent +(_ "(" ")" @end) @indent