Add PureScript LSP/Highlighting support (#6911)
This PR adds basic support for the language using the `purescript-language-server`. Release Notes: - Added support for PureScript.
This commit is contained in:
parent
7767062c09
commit
db68fc5130
9 changed files with 322 additions and 0 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -8549,6 +8549,15 @@ dependencies = [
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-purescript"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "git+https://github.com/ivanmoreau/tree-sitter-purescript?rev=a37140f0c7034977b90faa73c94fcb8a5e45ed08#a37140f0c7034977b90faa73c94fcb8a5e45ed08"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter-python"
|
name = "tree-sitter-python"
|
||||||
version = "0.20.4"
|
version = "0.20.4"
|
||||||
|
@ -9739,6 +9748,7 @@ dependencies = [
|
||||||
"tree-sitter-nix",
|
"tree-sitter-nix",
|
||||||
"tree-sitter-nu",
|
"tree-sitter-nu",
|
||||||
"tree-sitter-php",
|
"tree-sitter-php",
|
||||||
|
"tree-sitter-purescript",
|
||||||
"tree-sitter-python",
|
"tree-sitter-python",
|
||||||
"tree-sitter-racket",
|
"tree-sitter-racket",
|
||||||
"tree-sitter-ruby",
|
"tree-sitter-ruby",
|
||||||
|
|
|
@ -148,6 +148,7 @@ tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", re
|
||||||
tree-sitter-rust = "0.20.3"
|
tree-sitter-rust = "0.20.3"
|
||||||
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
|
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
|
||||||
tree-sitter-php = "0.21.1"
|
tree-sitter-php = "0.21.1"
|
||||||
|
tree-sitter-purescript = { git = "https://github.com/ivanmoreau/tree-sitter-purescript", rev = "a37140f0c7034977b90faa73c94fcb8a5e45ed08" }
|
||||||
tree-sitter-python = "0.20.2"
|
tree-sitter-python = "0.20.2"
|
||||||
tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" }
|
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-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }
|
||||||
|
|
|
@ -134,6 +134,7 @@ tree-sitter-ruby.workspace = true
|
||||||
tree-sitter-haskell.workspace = true
|
tree-sitter-haskell.workspace = true
|
||||||
tree-sitter-html.workspace = true
|
tree-sitter-html.workspace = true
|
||||||
tree-sitter-php.workspace = true
|
tree-sitter-php.workspace = true
|
||||||
|
tree-sitter-purescript.workspace = true
|
||||||
tree-sitter-scheme.workspace = true
|
tree-sitter-scheme.workspace = true
|
||||||
tree-sitter-svelte.workspace = true
|
tree-sitter-svelte.workspace = true
|
||||||
tree-sitter-racket.workspace = true
|
tree-sitter-racket.workspace = true
|
||||||
|
|
|
@ -23,6 +23,7 @@ mod language_plugin;
|
||||||
mod lua;
|
mod lua;
|
||||||
mod nu;
|
mod nu;
|
||||||
mod php;
|
mod php;
|
||||||
|
mod purescript;
|
||||||
mod python;
|
mod python;
|
||||||
mod ruby;
|
mod ruby;
|
||||||
mod rust;
|
mod rust;
|
||||||
|
@ -258,6 +259,13 @@ pub fn init(
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
language(
|
||||||
|
"purescript",
|
||||||
|
tree_sitter_purescript::language(),
|
||||||
|
vec![Arc::new(purescript::PurescriptLspAdapter::new(
|
||||||
|
node_runtime.clone(),
|
||||||
|
))],
|
||||||
|
);
|
||||||
language("elm", tree_sitter_elm::language(), vec![]);
|
language("elm", tree_sitter_elm::language(), vec![]);
|
||||||
language("glsl", tree_sitter_glsl::language(), vec![]);
|
language("glsl", tree_sitter_glsl::language(), vec![]);
|
||||||
language("nix", tree_sitter_nix::language(), vec![]);
|
language("nix", tree_sitter_nix::language(), vec![]);
|
||||||
|
|
139
crates/zed/src/languages/purescript.rs
Normal file
139
crates/zed/src/languages/purescript.rs
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use collections::HashMap;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
|
use lsp::LanguageServerBinary;
|
||||||
|
use node_runtime::NodeRuntime;
|
||||||
|
use serde_json::json;
|
||||||
|
use smol::fs;
|
||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
ffi::OsString,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use util::{async_maybe, ResultExt};
|
||||||
|
|
||||||
|
const SERVER_PATH: &'static str = "node_modules/.bin/purescript-language-server";
|
||||||
|
|
||||||
|
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||||
|
vec![server_path.into(), "--stdio".into()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PurescriptLspAdapter {
|
||||||
|
node: Arc<dyn NodeRuntime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PurescriptLspAdapter {
|
||||||
|
pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
|
||||||
|
Self { node }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for PurescriptLspAdapter {
|
||||||
|
fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("purescript-language-server".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"purescript"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
Ok(Box::new(
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version("purescript-language-server")
|
||||||
|
.await?,
|
||||||
|
) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let version = version.downcast::<String>().unwrap();
|
||||||
|
let server_path = container_dir.join(SERVER_PATH);
|
||||||
|
|
||||||
|
if fs::metadata(&server_path).await.is_err() {
|
||||||
|
self.node
|
||||||
|
.npm_install_packages(
|
||||||
|
&container_dir,
|
||||||
|
&[("purescript-language-server", version.as_str())],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: self.node.binary_path().await?,
|
||||||
|
arguments: server_binary_arguments(&server_path),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir, &*self.node).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir, &*self.node).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||||
|
Some(json!({
|
||||||
|
"purescript": {
|
||||||
|
"addSpagoSources": true
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn language_ids(&self) -> HashMap<String, String> {
|
||||||
|
[("PureScript".into(), "purescript".into())]
|
||||||
|
.into_iter()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(
|
||||||
|
container_dir: PathBuf,
|
||||||
|
node: &dyn NodeRuntime,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
async_maybe!({
|
||||||
|
let mut last_version_dir = None;
|
||||||
|
let mut entries = fs::read_dir(&container_dir).await?;
|
||||||
|
while let Some(entry) = entries.next().await {
|
||||||
|
let entry = entry?;
|
||||||
|
if entry.file_type().await?.is_dir() {
|
||||||
|
last_version_dir = Some(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||||
|
let server_path = last_version_dir.join(SERVER_PATH);
|
||||||
|
if server_path.exists() {
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: node.binary_path().await?,
|
||||||
|
arguments: server_binary_arguments(&server_path),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"missing executable in directory {:?}",
|
||||||
|
last_version_dir
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
}
|
3
crates/zed/src/languages/purescript/brackets.scm
Normal file
3
crates/zed/src/languages/purescript/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("(" @open ")" @close)
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
13
crates/zed/src/languages/purescript/config.toml
Normal file
13
crates/zed/src/languages/purescript/config.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name = "PureScript"
|
||||||
|
path_suffixes = ["purs"]
|
||||||
|
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 },
|
||||||
|
]
|
144
crates/zed/src/languages/purescript/highlights.scm
Normal file
144
crates/zed/src/languages/purescript/highlights.scm
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
;; 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 (number)) @float
|
||||||
|
(char) @character
|
||||||
|
[
|
||||||
|
(string)
|
||||||
|
(triple_quote_string)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
|
||||||
|
;; ----------------------------------------------------------------------------
|
||||||
|
;; Punctuation
|
||||||
|
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
(comma)
|
||||||
|
";"
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
|
||||||
|
;; ----------------------------------------------------------------------------
|
||||||
|
;; Keywords, operators, includes
|
||||||
|
|
||||||
|
[
|
||||||
|
"forall"
|
||||||
|
"∀"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
;; (pragma) @constant
|
||||||
|
|
||||||
|
[
|
||||||
|
"if"
|
||||||
|
"then"
|
||||||
|
"else"
|
||||||
|
"case"
|
||||||
|
"of"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
"import"
|
||||||
|
"module"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
(operator)
|
||||||
|
(constructor_operator)
|
||||||
|
(type_operator)
|
||||||
|
(qualified_module) ; grabs the `.` (dot), ex: import System.IO
|
||||||
|
(all_names)
|
||||||
|
(wildcard)
|
||||||
|
"="
|
||||||
|
"|"
|
||||||
|
"::"
|
||||||
|
"=>"
|
||||||
|
"->"
|
||||||
|
"<-"
|
||||||
|
"\\"
|
||||||
|
"`"
|
||||||
|
"@"
|
||||||
|
"∷"
|
||||||
|
"⇒"
|
||||||
|
"<="
|
||||||
|
"⇐"
|
||||||
|
"→"
|
||||||
|
"←"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
(module) @title
|
||||||
|
|
||||||
|
[
|
||||||
|
(where)
|
||||||
|
"let"
|
||||||
|
"in"
|
||||||
|
"class"
|
||||||
|
"instance"
|
||||||
|
"derive"
|
||||||
|
"foreign"
|
||||||
|
"data"
|
||||||
|
"newtype"
|
||||||
|
"type"
|
||||||
|
"as"
|
||||||
|
"hiding"
|
||||||
|
"do"
|
||||||
|
"ado"
|
||||||
|
"infix"
|
||||||
|
"infixl"
|
||||||
|
"infixr"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
|
||||||
|
;; ----------------------------------------------------------------------------
|
||||||
|
;; Functions and variables
|
||||||
|
|
||||||
|
(variable) @variable
|
||||||
|
(pat_wildcard) @variable
|
||||||
|
|
||||||
|
(signature name: (variable) @type)
|
||||||
|
(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)))
|
||||||
|
|
||||||
|
|
||||||
|
;; ----------------------------------------------------------------------------
|
||||||
|
;; Types
|
||||||
|
|
||||||
|
(type) @type
|
||||||
|
(type_variable) @type
|
||||||
|
|
||||||
|
(constructor) @constructor
|
||||||
|
|
||||||
|
; True or False
|
||||||
|
((constructor) @_bool (#match? @_bool "(True|False)")) @boolean
|
3
crates/zed/src/languages/purescript/indents.scm
Normal file
3
crates/zed/src/languages/purescript/indents.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
(_ "[" "]" @end) @indent
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
Loading…
Add table
Add a link
Reference in a new issue