Port zed/src/languages
to zed2
This commit is contained in:
parent
58446c2715
commit
db34de6be4
191 changed files with 9448 additions and 182 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -10946,7 +10946,7 @@ dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"db2",
|
"db2",
|
||||||
"env_logger 0.9.3",
|
"env_logger 0.9.3",
|
||||||
"feature_flags",
|
"feature_flags2",
|
||||||
"fs2",
|
"fs2",
|
||||||
"fsevent",
|
"fsevent",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
|
@ -10963,7 +10963,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp2",
|
||||||
"node_runtime",
|
"node_runtime",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
|
@ -11016,6 +11016,7 @@ dependencies = [
|
||||||
"tree-sitter-svelte",
|
"tree-sitter-svelte",
|
||||||
"tree-sitter-toml",
|
"tree-sitter-toml",
|
||||||
"tree-sitter-typescript",
|
"tree-sitter-typescript",
|
||||||
|
"tree-sitter-vue",
|
||||||
"tree-sitter-yaml",
|
"tree-sitter-yaml",
|
||||||
"unindent",
|
"unindent",
|
||||||
"url",
|
"url",
|
||||||
|
|
|
@ -637,6 +637,10 @@ impl AppContext {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = SharedString> + 'a {
|
||||||
|
self.action_builders.keys().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// Move the global of the given type to the stack.
|
/// Move the global of the given type to the stack.
|
||||||
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
|
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
|
||||||
GlobalLease::new(
|
GlobalLease::new(
|
||||||
|
|
|
@ -230,8 +230,8 @@ impl CachedLspAdapter {
|
||||||
self.adapter.label_for_symbol(name, kind, language).await
|
self.adapter.label_for_symbol(name, kind, language).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enabled_formatters(&self) -> Vec<BundledFormatter> {
|
pub fn prettier_plugins(&self) -> &[&'static str] {
|
||||||
self.adapter.enabled_formatters()
|
self.adapter.prettier_plugins()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,31 +340,8 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enabled_formatters(&self) -> Vec<BundledFormatter> {
|
fn prettier_plugins(&self) -> &[&'static str] {
|
||||||
Vec::new()
|
&[]
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum BundledFormatter {
|
|
||||||
Prettier {
|
|
||||||
// See https://prettier.io/docs/en/options.html#parser for a list of valid values.
|
|
||||||
// Usually, every language has a single parser (standard or plugin-provided), hence `Some("parser_name")` can be used.
|
|
||||||
// There can not be multiple parsers for a single language, in case of a conflict, we would attempt to select the one with most plugins.
|
|
||||||
//
|
|
||||||
// But exceptions like Tailwind CSS exist, which uses standard parsers for CSS/JS/HTML/etc. but require an extra plugin to be installed.
|
|
||||||
// For those cases, `None` will install the plugin but apply other, regular parser defined for the language, and this would not be a conflict.
|
|
||||||
parser_name: Option<&'static str>,
|
|
||||||
plugin_names: Vec<&'static str>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BundledFormatter {
|
|
||||||
pub fn prettier(parser_name: &'static str) -> Self {
|
|
||||||
Self::Prettier {
|
|
||||||
parser_name: Some(parser_name),
|
|
||||||
plugin_names: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,6 +379,8 @@ pub struct LanguageConfig {
|
||||||
pub overrides: HashMap<String, LanguageConfigOverride>,
|
pub overrides: HashMap<String, LanguageConfigOverride>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub word_characters: HashSet<char>,
|
pub word_characters: HashSet<char>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub prettier_parser_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -475,6 +454,7 @@ impl Default for LanguageConfig {
|
||||||
overrides: Default::default(),
|
overrides: Default::default(),
|
||||||
collapsed_placeholder: Default::default(),
|
collapsed_placeholder: Default::default(),
|
||||||
word_characters: Default::default(),
|
word_characters: Default::default(),
|
||||||
|
prettier_parser_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,7 +480,7 @@ pub struct FakeLspAdapter {
|
||||||
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp2::FakeLanguageServer)>>,
|
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp2::FakeLanguageServer)>>,
|
||||||
pub disk_based_diagnostics_progress_token: Option<String>,
|
pub disk_based_diagnostics_progress_token: Option<String>,
|
||||||
pub disk_based_diagnostics_sources: Vec<String>,
|
pub disk_based_diagnostics_sources: Vec<String>,
|
||||||
pub enabled_formatters: Vec<BundledFormatter>,
|
pub prettier_plugins: Vec<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -1604,6 +1584,10 @@ impl Language {
|
||||||
override_id: None,
|
override_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prettier_parser_name(&self) -> Option<&str> {
|
||||||
|
self.config.prettier_parser_name.as_deref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageScope {
|
impl LanguageScope {
|
||||||
|
@ -1766,7 +1750,7 @@ impl Default for FakeLspAdapter {
|
||||||
disk_based_diagnostics_progress_token: None,
|
disk_based_diagnostics_progress_token: None,
|
||||||
initialization_options: None,
|
initialization_options: None,
|
||||||
disk_based_diagnostics_sources: Vec::new(),
|
disk_based_diagnostics_sources: Vec::new(),
|
||||||
enabled_formatters: Vec::new(),
|
prettier_plugins: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1824,8 +1808,8 @@ impl LspAdapter for Arc<FakeLspAdapter> {
|
||||||
self.initialization_options.clone()
|
self.initialization_options.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enabled_formatters(&self) -> Vec<BundledFormatter> {
|
fn prettier_plugins(&self) -> &[&'static str] {
|
||||||
self.enabled_formatters.clone()
|
&self.prettier_plugins
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::HashMap;
|
||||||
use fs2::Fs;
|
use fs2::Fs;
|
||||||
use gpui2::{AsyncAppContext, Model};
|
use gpui2::{AsyncAppContext, Model};
|
||||||
use language2::{language_settings::language_settings, Buffer, BundledFormatter, Diff};
|
use language2::{language_settings::language_settings, Buffer, Diff};
|
||||||
use lsp2::{LanguageServer, LanguageServerId};
|
use lsp2::{LanguageServer, LanguageServerId};
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -189,128 +189,134 @@ impl Prettier {
|
||||||
) -> anyhow::Result<Diff> {
|
) -> anyhow::Result<Diff> {
|
||||||
match self {
|
match self {
|
||||||
Self::Real(local) => {
|
Self::Real(local) => {
|
||||||
let params = buffer.update(cx, |buffer, cx| {
|
let params = buffer
|
||||||
let buffer_language = buffer.language();
|
.update(cx, |buffer, cx| {
|
||||||
let parsers_with_plugins = buffer_language
|
let buffer_language = buffer.language();
|
||||||
.into_iter()
|
let parser_with_plugins = buffer_language.and_then(|l| {
|
||||||
.flat_map(|language| {
|
let prettier_parser = l.prettier_parser_name()?;
|
||||||
language
|
let mut prettier_plugins = l
|
||||||
.lsp_adapters()
|
.lsp_adapters()
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|adapter| adapter.enabled_formatters())
|
.flat_map(|adapter| adapter.prettier_plugins())
|
||||||
.filter_map(|formatter| match formatter {
|
.collect::<Vec<_>>();
|
||||||
BundledFormatter::Prettier {
|
prettier_plugins.dedup();
|
||||||
parser_name,
|
Some((prettier_parser, prettier_plugins))
|
||||||
plugin_names,
|
});
|
||||||
} => Some((parser_name, plugin_names)),
|
|
||||||
})
|
let prettier_node_modules = self.prettier_dir().join("node_modules");
|
||||||
})
|
anyhow::ensure!(
|
||||||
.fold(
|
prettier_node_modules.is_dir(),
|
||||||
HashMap::default(),
|
"Prettier node_modules dir does not exist: {prettier_node_modules:?}"
|
||||||
|mut parsers_with_plugins, (parser_name, plugins)| {
|
);
|
||||||
match parser_name {
|
let plugin_name_into_path = |plugin_name: &str| {
|
||||||
Some(parser_name) => parsers_with_plugins
|
let prettier_plugin_dir = prettier_node_modules.join(plugin_name);
|
||||||
.entry(parser_name)
|
for possible_plugin_path in [
|
||||||
.or_insert_with(HashSet::default)
|
prettier_plugin_dir.join("dist").join("index.mjs"),
|
||||||
.extend(plugins),
|
prettier_plugin_dir.join("dist").join("index.js"),
|
||||||
None => parsers_with_plugins.values_mut().for_each(|existing_plugins| {
|
prettier_plugin_dir.join("dist").join("plugin.js"),
|
||||||
existing_plugins.extend(plugins.iter());
|
prettier_plugin_dir.join("index.mjs"),
|
||||||
}),
|
prettier_plugin_dir.join("index.js"),
|
||||||
|
prettier_plugin_dir.join("plugin.js"),
|
||||||
|
prettier_plugin_dir,
|
||||||
|
] {
|
||||||
|
if possible_plugin_path.is_file() {
|
||||||
|
return Some(possible_plugin_path);
|
||||||
}
|
}
|
||||||
parsers_with_plugins
|
}
|
||||||
},
|
None
|
||||||
|
};
|
||||||
|
let (parser, located_plugins) = match parser_with_plugins {
|
||||||
|
Some((parser, plugins)) => {
|
||||||
|
// Tailwind plugin requires being added last
|
||||||
|
// https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins
|
||||||
|
let mut add_tailwind_back = false;
|
||||||
|
|
||||||
|
let mut plugins = plugins
|
||||||
|
.into_iter()
|
||||||
|
.filter(|&&plugin_name| {
|
||||||
|
if plugin_name == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME {
|
||||||
|
add_tailwind_back = true;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|plugin_name| {
|
||||||
|
(plugin_name, plugin_name_into_path(plugin_name))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if add_tailwind_back {
|
||||||
|
plugins.push((
|
||||||
|
&TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME,
|
||||||
|
plugin_name_into_path(
|
||||||
|
TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(Some(parser.to_string()), plugins)
|
||||||
|
}
|
||||||
|
None => (None, Vec::new()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let prettier_options = if self.is_default() {
|
||||||
|
let language_settings =
|
||||||
|
language_settings(buffer_language, buffer.file(), cx);
|
||||||
|
let mut options = language_settings.prettier.clone();
|
||||||
|
if !options.contains_key("tabWidth") {
|
||||||
|
options.insert(
|
||||||
|
"tabWidth".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(
|
||||||
|
language_settings.tab_size.get(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !options.contains_key("printWidth") {
|
||||||
|
options.insert(
|
||||||
|
"printWidth".to_string(),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(
|
||||||
|
language_settings.preferred_line_length,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(options)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let plugins = located_plugins
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(plugin_name, located_plugin_path)| {
|
||||||
|
match located_plugin_path {
|
||||||
|
Some(path) => Some(path),
|
||||||
|
None => {
|
||||||
|
log::error!(
|
||||||
|
"Have not found plugin path for {:?} inside {:?}",
|
||||||
|
plugin_name,
|
||||||
|
prettier_node_modules
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
log::debug!(
|
||||||
|
"Formatting file {:?} with prettier, plugins :{:?}, options: {:?}",
|
||||||
|
plugins,
|
||||||
|
prettier_options,
|
||||||
|
buffer.file().map(|f| f.full_path(cx))
|
||||||
);
|
);
|
||||||
|
|
||||||
let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len());
|
anyhow::Ok(FormatParams {
|
||||||
if parsers_with_plugins.len() > 1 {
|
text: buffer.text(),
|
||||||
log::warn!("Found multiple parsers with plugins {parsers_with_plugins:?}, will select only one: {selected_parser_with_plugins:?}");
|
options: FormatOptions {
|
||||||
}
|
parser,
|
||||||
|
plugins,
|
||||||
let prettier_node_modules = self.prettier_dir().join("node_modules");
|
path: buffer_path,
|
||||||
anyhow::ensure!(prettier_node_modules.is_dir(), "Prettier node_modules dir does not exist: {prettier_node_modules:?}");
|
prettier_options,
|
||||||
let plugin_name_into_path = |plugin_name: &str| {
|
},
|
||||||
let prettier_plugin_dir = prettier_node_modules.join(plugin_name);
|
})
|
||||||
for possible_plugin_path in [
|
})?
|
||||||
prettier_plugin_dir.join("dist").join("index.mjs"),
|
.context("prettier params calculation")?;
|
||||||
prettier_plugin_dir.join("dist").join("index.js"),
|
|
||||||
prettier_plugin_dir.join("dist").join("plugin.js"),
|
|
||||||
prettier_plugin_dir.join("index.mjs"),
|
|
||||||
prettier_plugin_dir.join("index.js"),
|
|
||||||
prettier_plugin_dir.join("plugin.js"),
|
|
||||||
prettier_plugin_dir,
|
|
||||||
] {
|
|
||||||
if possible_plugin_path.is_file() {
|
|
||||||
return Some(possible_plugin_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let (parser, located_plugins) = match selected_parser_with_plugins {
|
|
||||||
Some((parser, plugins)) => {
|
|
||||||
// Tailwind plugin requires being added last
|
|
||||||
// https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins
|
|
||||||
let mut add_tailwind_back = false;
|
|
||||||
|
|
||||||
let mut plugins = plugins.into_iter().filter(|&&plugin_name| {
|
|
||||||
if plugin_name == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME {
|
|
||||||
add_tailwind_back = true;
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}).map(|plugin_name| (plugin_name, plugin_name_into_path(plugin_name))).collect::<Vec<_>>();
|
|
||||||
if add_tailwind_back {
|
|
||||||
plugins.push((&TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME, plugin_name_into_path(TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME)));
|
|
||||||
}
|
|
||||||
(Some(parser.to_string()), plugins)
|
|
||||||
},
|
|
||||||
None => (None, Vec::new()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let prettier_options = if self.is_default() {
|
|
||||||
let language_settings = language_settings(buffer_language, buffer.file(), cx);
|
|
||||||
let mut options = language_settings.prettier.clone();
|
|
||||||
if !options.contains_key("tabWidth") {
|
|
||||||
options.insert(
|
|
||||||
"tabWidth".to_string(),
|
|
||||||
serde_json::Value::Number(serde_json::Number::from(
|
|
||||||
language_settings.tab_size.get(),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if !options.contains_key("printWidth") {
|
|
||||||
options.insert(
|
|
||||||
"printWidth".to_string(),
|
|
||||||
serde_json::Value::Number(serde_json::Number::from(
|
|
||||||
language_settings.preferred_line_length,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(options)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let plugins = located_plugins.into_iter().filter_map(|(plugin_name, located_plugin_path)| {
|
|
||||||
match located_plugin_path {
|
|
||||||
Some(path) => Some(path),
|
|
||||||
None => {
|
|
||||||
log::error!("Have not found plugin path for {plugin_name:?} inside {prettier_node_modules:?}");
|
|
||||||
None},
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
log::debug!("Formatting file {:?} with prettier, plugins :{plugins:?}, options: {prettier_options:?}", buffer.file().map(|f| f.full_path(cx)));
|
|
||||||
|
|
||||||
anyhow::Ok(FormatParams {
|
|
||||||
text: buffer.text(),
|
|
||||||
options: FormatOptions {
|
|
||||||
parser,
|
|
||||||
plugins,
|
|
||||||
path: buffer_path,
|
|
||||||
prettier_options,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})?.context("prettier params calculation")?;
|
|
||||||
let response = local
|
let response = local
|
||||||
.server
|
.server
|
||||||
.request::<Format>(params)
|
.request::<Format>(params)
|
||||||
|
|
|
@ -39,11 +39,11 @@ use language2::{
|
||||||
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
||||||
serialize_anchor, serialize_version, split_operations,
|
serialize_anchor, serialize_version, split_operations,
|
||||||
},
|
},
|
||||||
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, BundledFormatter, CachedLspAdapter,
|
range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeAction,
|
||||||
CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff,
|
CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent,
|
||||||
Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile,
|
File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate,
|
||||||
LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16,
|
OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot,
|
||||||
TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
use lsp2::{
|
use lsp2::{
|
||||||
|
@ -8410,12 +8410,7 @@ impl Project {
|
||||||
let Some(buffer_language) = buffer.language() else {
|
let Some(buffer_language) = buffer.language() else {
|
||||||
return Task::ready(None);
|
return Task::ready(None);
|
||||||
};
|
};
|
||||||
if !buffer_language
|
if buffer_language.prettier_parser_name().is_none() {
|
||||||
.lsp_adapters()
|
|
||||||
.iter()
|
|
||||||
.flat_map(|adapter| adapter.enabled_formatters())
|
|
||||||
.any(|formatter| matches!(formatter, BundledFormatter::Prettier { .. }))
|
|
||||||
{
|
|
||||||
return Task::ready(None);
|
return Task::ready(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8574,16 +8569,15 @@ impl Project {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut prettier_plugins = None;
|
let mut prettier_plugins = None;
|
||||||
for formatter in new_language
|
if new_language.prettier_parser_name().is_some() {
|
||||||
.lsp_adapters()
|
prettier_plugins
|
||||||
.into_iter()
|
.get_or_insert_with(|| HashSet::default())
|
||||||
.flat_map(|adapter| adapter.enabled_formatters())
|
.extend(
|
||||||
{
|
new_language
|
||||||
match formatter {
|
.lsp_adapters()
|
||||||
BundledFormatter::Prettier { plugin_names, .. } => prettier_plugins
|
.iter()
|
||||||
.get_or_insert_with(|| HashSet::default())
|
.flat_map(|adapter| adapter.prettier_plugins()),
|
||||||
.extend(plugin_names),
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let Some(prettier_plugins) = prettier_plugins else {
|
let Some(prettier_plugins) = prettier_plugins else {
|
||||||
return Task::ready(Ok(()));
|
return Task::ready(Ok(()));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
|
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
use gpui2::{AppContext, KeyBinding};
|
use gpui2::{AppContext, KeyBinding, SharedString};
|
||||||
use schemars::{
|
use schemars::{
|
||||||
gen::{SchemaGenerator, SchemaSettings},
|
gen::{SchemaGenerator, SchemaSettings},
|
||||||
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
|
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
|
||||||
|
@ -96,7 +96,7 @@ impl KeymapFile {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_json_schema(action_names: &[&'static str]) -> serde_json::Value {
|
pub fn generate_json_schema(action_names: &[SharedString]) -> serde_json::Value {
|
||||||
let mut root_schema = SchemaSettings::draft07()
|
let mut root_schema = SchemaSettings::draft07()
|
||||||
.with(|settings| settings.option_add_null_type = false)
|
.with(|settings| settings.option_add_null_type = false)
|
||||||
.into_generator()
|
.into_generator()
|
||||||
|
|
|
@ -108,6 +108,24 @@ pub struct SyntaxTheme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxTheme {
|
impl SyntaxTheme {
|
||||||
|
// TOOD: Get this working with `#[cfg(test)]`. Why isn't it?
|
||||||
|
pub fn new_test(colors: impl IntoIterator<Item = (&'static str, Hsla)>) -> Self {
|
||||||
|
SyntaxTheme {
|
||||||
|
highlights: colors
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, color)| {
|
||||||
|
(
|
||||||
|
key.to_owned(),
|
||||||
|
HighlightStyle {
|
||||||
|
color: Some(color),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, name: &str) -> HighlightStyle {
|
pub fn get(&self, name: &str) -> HighlightStyle {
|
||||||
self.highlights
|
self.highlights
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -47,7 +47,7 @@ install_cli = { path = "../install_cli" }
|
||||||
journal2 = { path = "../journal2" }
|
journal2 = { path = "../journal2" }
|
||||||
language2 = { path = "../language2" }
|
language2 = { path = "../language2" }
|
||||||
# language_selector = { path = "../language_selector" }
|
# language_selector = { path = "../language_selector" }
|
||||||
lsp = { path = "../lsp" }
|
lsp2 = { path = "../lsp2" }
|
||||||
language_tools = { path = "../language_tools" }
|
language_tools = { path = "../language_tools" }
|
||||||
node_runtime = { path = "../node_runtime" }
|
node_runtime = { path = "../node_runtime" }
|
||||||
# assistant = { path = "../assistant" }
|
# assistant = { path = "../assistant" }
|
||||||
|
@ -60,7 +60,7 @@ project2 = { path = "../project2" }
|
||||||
# recent_projects = { path = "../recent_projects" }
|
# recent_projects = { path = "../recent_projects" }
|
||||||
rpc2 = { path = "../rpc2" }
|
rpc2 = { path = "../rpc2" }
|
||||||
settings2 = { path = "../settings2" }
|
settings2 = { path = "../settings2" }
|
||||||
feature_flags = { path = "../feature_flags" }
|
feature_flags2 = { path = "../feature_flags2" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
shellexpand = "2.1.0"
|
shellexpand = "2.1.0"
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
|
@ -135,6 +135,7 @@ tree-sitter-yaml.workspace = true
|
||||||
tree-sitter-lua.workspace = true
|
tree-sitter-lua.workspace = true
|
||||||
tree-sitter-nix.workspace = true
|
tree-sitter-nix.workspace = true
|
||||||
tree-sitter-nu.workspace = true
|
tree-sitter-nu.workspace = true
|
||||||
|
tree-sitter-vue.workspace = true
|
||||||
|
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
urlencoding = "2.1.2"
|
urlencoding = "2.1.2"
|
||||||
|
|
273
crates/zed2/src/languages.rs
Normal file
273
crates/zed2/src/languages.rs
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
use anyhow::Context;
|
||||||
|
use gpui2::AppContext;
|
||||||
|
pub use language2::*;
|
||||||
|
use node_runtime::NodeRuntime;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
use settings2::Settings;
|
||||||
|
use std::{borrow::Cow, str, sync::Arc};
|
||||||
|
use util::asset_str;
|
||||||
|
|
||||||
|
use self::elixir::ElixirSettings;
|
||||||
|
|
||||||
|
mod c;
|
||||||
|
mod css;
|
||||||
|
mod elixir;
|
||||||
|
mod go;
|
||||||
|
mod html;
|
||||||
|
mod json;
|
||||||
|
#[cfg(feature = "plugin_runtime")]
|
||||||
|
mod language_plugin;
|
||||||
|
mod lua;
|
||||||
|
mod php;
|
||||||
|
mod python;
|
||||||
|
mod ruby;
|
||||||
|
mod rust;
|
||||||
|
mod svelte;
|
||||||
|
mod tailwind;
|
||||||
|
mod typescript;
|
||||||
|
mod vue;
|
||||||
|
mod yaml;
|
||||||
|
|
||||||
|
// 1. Add tree-sitter-{language} parser to zed crate
|
||||||
|
// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
|
||||||
|
// 3. Add config.toml to the newly created language directory using existing languages as a template
|
||||||
|
// 4. Copy highlights from tree sitter repo for the language into a highlights.scm file.
|
||||||
|
// Note: github highlights take the last match while zed takes the first
|
||||||
|
// 5. Add indents.scm, outline.scm, and brackets.scm to implement indent on newline, outline/breadcrumbs,
|
||||||
|
// and autoclosing brackets respectively
|
||||||
|
// 6. If the language has injections add an injections.scm query file
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "src/languages"]
|
||||||
|
#[exclude = "*.rs"]
|
||||||
|
struct LanguageDir;
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
languages: Arc<LanguageRegistry>,
|
||||||
|
node_runtime: Arc<dyn NodeRuntime>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) {
|
||||||
|
ElixirSettings::register(cx);
|
||||||
|
|
||||||
|
let language = |name, grammar, adapters| {
|
||||||
|
languages.register(name, load_config(name), grammar, adapters, load_queries)
|
||||||
|
};
|
||||||
|
|
||||||
|
language("bash", tree_sitter_bash::language(), vec![]);
|
||||||
|
language(
|
||||||
|
"c",
|
||||||
|
tree_sitter_c::language(),
|
||||||
|
vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"cpp",
|
||||||
|
tree_sitter_cpp::language(),
|
||||||
|
vec![Arc::new(c::CLspAdapter)],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"css",
|
||||||
|
tree_sitter_css::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
match &ElixirSettings::get(None, cx).lsp {
|
||||||
|
elixir::ElixirLspSetting::ElixirLs => language(
|
||||||
|
"elixir",
|
||||||
|
tree_sitter_elixir::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(elixir::ElixirLspAdapter),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
elixir::ElixirLspSetting::NextLs => language(
|
||||||
|
"elixir",
|
||||||
|
tree_sitter_elixir::language(),
|
||||||
|
vec![Arc::new(elixir::NextLspAdapter)],
|
||||||
|
),
|
||||||
|
elixir::ElixirLspSetting::Local { path, arguments } => language(
|
||||||
|
"elixir",
|
||||||
|
tree_sitter_elixir::language(),
|
||||||
|
vec![Arc::new(elixir::LocalLspAdapter {
|
||||||
|
path: path.clone(),
|
||||||
|
arguments: arguments.clone(),
|
||||||
|
})],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
language(
|
||||||
|
"go",
|
||||||
|
tree_sitter_go::language(),
|
||||||
|
vec![Arc::new(go::GoLspAdapter)],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"heex",
|
||||||
|
tree_sitter_heex::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(elixir::ElixirLspAdapter),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"json",
|
||||||
|
tree_sitter_json::language(),
|
||||||
|
vec![Arc::new(json::JsonLspAdapter::new(
|
||||||
|
node_runtime.clone(),
|
||||||
|
languages.clone(),
|
||||||
|
))],
|
||||||
|
);
|
||||||
|
language("markdown", tree_sitter_markdown::language(), vec![]);
|
||||||
|
language(
|
||||||
|
"python",
|
||||||
|
tree_sitter_python::language(),
|
||||||
|
vec![Arc::new(python::PythonLspAdapter::new(
|
||||||
|
node_runtime.clone(),
|
||||||
|
))],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"rust",
|
||||||
|
tree_sitter_rust::language(),
|
||||||
|
vec![Arc::new(rust::RustLspAdapter)],
|
||||||
|
);
|
||||||
|
language("toml", tree_sitter_toml::language(), vec![]);
|
||||||
|
language(
|
||||||
|
"tsx",
|
||||||
|
tree_sitter_typescript::language_tsx(),
|
||||||
|
vec![
|
||||||
|
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"typescript",
|
||||||
|
tree_sitter_typescript::language_typescript(),
|
||||||
|
vec![
|
||||||
|
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"javascript",
|
||||||
|
tree_sitter_typescript::language_tsx(),
|
||||||
|
vec![
|
||||||
|
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"html",
|
||||||
|
tree_sitter_html::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"ruby",
|
||||||
|
tree_sitter_ruby::language(),
|
||||||
|
vec![Arc::new(ruby::RubyLanguageServer)],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"erb",
|
||||||
|
tree_sitter_embedded_template::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(ruby::RubyLanguageServer),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language("scheme", tree_sitter_scheme::language(), vec![]);
|
||||||
|
language("racket", tree_sitter_racket::language(), vec![]);
|
||||||
|
language(
|
||||||
|
"lua",
|
||||||
|
tree_sitter_lua::language(),
|
||||||
|
vec![Arc::new(lua::LuaLspAdapter)],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"yaml",
|
||||||
|
tree_sitter_yaml::language(),
|
||||||
|
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"svelte",
|
||||||
|
tree_sitter_svelte::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
language(
|
||||||
|
"php",
|
||||||
|
tree_sitter_php::language(),
|
||||||
|
vec![
|
||||||
|
Arc::new(php::IntelephenseLspAdapter::new(node_runtime.clone())),
|
||||||
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
language("elm", tree_sitter_elm::language(), vec![]);
|
||||||
|
language("glsl", tree_sitter_glsl::language(), vec![]);
|
||||||
|
language("nix", tree_sitter_nix::language(), vec![]);
|
||||||
|
language("nu", tree_sitter_nu::language(), vec![]);
|
||||||
|
language(
|
||||||
|
"vue",
|
||||||
|
tree_sitter_vue::language(),
|
||||||
|
vec![Arc::new(vue::VueLspAdapter::new(node_runtime))],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub async fn language(
|
||||||
|
name: &str,
|
||||||
|
grammar: tree_sitter::Language,
|
||||||
|
lsp_adapter: Option<Arc<dyn LspAdapter>>,
|
||||||
|
) -> Arc<Language> {
|
||||||
|
Arc::new(
|
||||||
|
Language::new(load_config(name), Some(grammar))
|
||||||
|
.with_lsp_adapters(lsp_adapter.into_iter().collect())
|
||||||
|
.await
|
||||||
|
.with_queries(load_queries(name))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_config(name: &str) -> LanguageConfig {
|
||||||
|
toml::from_slice(
|
||||||
|
&LanguageDir::get(&format!("{}/config.toml", name))
|
||||||
|
.unwrap()
|
||||||
|
.data,
|
||||||
|
)
|
||||||
|
.with_context(|| format!("failed to load config.toml for language {name:?}"))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_queries(name: &str) -> LanguageQueries {
|
||||||
|
LanguageQueries {
|
||||||
|
highlights: load_query(name, "/highlights"),
|
||||||
|
brackets: load_query(name, "/brackets"),
|
||||||
|
indents: load_query(name, "/indents"),
|
||||||
|
outline: load_query(name, "/outline"),
|
||||||
|
embedding: load_query(name, "/embedding"),
|
||||||
|
injections: load_query(name, "/injections"),
|
||||||
|
overrides: load_query(name, "/overrides"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
|
||||||
|
let mut result = None;
|
||||||
|
for path in LanguageDir::iter() {
|
||||||
|
if let Some(remainder) = path.strip_prefix(name) {
|
||||||
|
if remainder.starts_with(filename_prefix) {
|
||||||
|
let contents = asset_str::<LanguageDir>(path.as_ref());
|
||||||
|
match &mut result {
|
||||||
|
None => result = Some(contents),
|
||||||
|
Some(r) => r.to_mut().push_str(contents.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
3
crates/zed2/src/languages/bash/brackets.scm
Normal file
3
crates/zed2/src/languages/bash/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("(" @open ")" @close)
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
9
crates/zed2/src/languages/bash/config.toml
Normal file
9
crates/zed2/src/languages/bash/config.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
name = "Shell Script"
|
||||||
|
path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin", "zprofile"]
|
||||||
|
line_comment = "# "
|
||||||
|
first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b"
|
||||||
|
brackets = [
|
||||||
|
{ start = "[", end = "]", close = true, newline = false },
|
||||||
|
{ start = "(", end = ")", close = true, newline = false },
|
||||||
|
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
]
|
59
crates/zed2/src/languages/bash/highlights.scm
Normal file
59
crates/zed2/src/languages/bash/highlights.scm
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
[
|
||||||
|
(string)
|
||||||
|
(raw_string)
|
||||||
|
(heredoc_body)
|
||||||
|
(heredoc_start)
|
||||||
|
(ansi_c_string)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
(command_name) @function
|
||||||
|
|
||||||
|
(variable_name) @property
|
||||||
|
|
||||||
|
[
|
||||||
|
"case"
|
||||||
|
"do"
|
||||||
|
"done"
|
||||||
|
"elif"
|
||||||
|
"else"
|
||||||
|
"esac"
|
||||||
|
"export"
|
||||||
|
"fi"
|
||||||
|
"for"
|
||||||
|
"function"
|
||||||
|
"if"
|
||||||
|
"in"
|
||||||
|
"select"
|
||||||
|
"then"
|
||||||
|
"unset"
|
||||||
|
"until"
|
||||||
|
"while"
|
||||||
|
"local"
|
||||||
|
"declare"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
(function_definition name: (word) @function)
|
||||||
|
|
||||||
|
(file_descriptor) @number
|
||||||
|
|
||||||
|
[
|
||||||
|
(command_substitution)
|
||||||
|
(process_substitution)
|
||||||
|
(expansion)
|
||||||
|
]@embedded
|
||||||
|
|
||||||
|
[
|
||||||
|
"$"
|
||||||
|
"&&"
|
||||||
|
">"
|
||||||
|
">>"
|
||||||
|
"<"
|
||||||
|
"|"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
(
|
||||||
|
(command (_) @constant)
|
||||||
|
(#match? @constant "^-")
|
||||||
|
)
|
321
crates/zed2/src/languages/c.rs
Normal file
321
crates/zed2/src/languages/c.rs
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::StreamExt;
|
||||||
|
pub use language2::*;
|
||||||
|
use lsp2::LanguageServerBinary;
|
||||||
|
use smol::fs::{self, File};
|
||||||
|
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||||
|
use util::{
|
||||||
|
fs::remove_matching,
|
||||||
|
github::{latest_github_release, GitHubLspBinaryVersion},
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CLspAdapter;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl super::LspAdapter for CLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("clangd".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"clangd"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
let release = latest_github_release("clangd/clangd", false, delegate.http_client()).await?;
|
||||||
|
let asset_name = format!("clangd-mac-{}.zip", release.name);
|
||||||
|
let asset = release
|
||||||
|
.assets
|
||||||
|
.iter()
|
||||||
|
.find(|asset| asset.name == asset_name)
|
||||||
|
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||||
|
let version = GitHubLspBinaryVersion {
|
||||||
|
name: release.name,
|
||||||
|
url: asset.browser_download_url.clone(),
|
||||||
|
};
|
||||||
|
Ok(Box::new(version) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||||
|
let zip_path = container_dir.join(format!("clangd_{}.zip", version.name));
|
||||||
|
let version_dir = container_dir.join(format!("clangd_{}", version.name));
|
||||||
|
let binary_path = version_dir.join("bin/clangd");
|
||||||
|
|
||||||
|
if fs::metadata(&binary_path).await.is_err() {
|
||||||
|
let mut response = delegate
|
||||||
|
.http_client()
|
||||||
|
.get(&version.url, Default::default(), true)
|
||||||
|
.await
|
||||||
|
.context("error downloading release")?;
|
||||||
|
let mut file = File::create(&zip_path).await?;
|
||||||
|
if !response.status().is_success() {
|
||||||
|
Err(anyhow!(
|
||||||
|
"download failed with status {}",
|
||||||
|
response.status().to_string()
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
futures::io::copy(response.body_mut(), &mut file).await?;
|
||||||
|
|
||||||
|
let unzip_status = smol::process::Command::new("unzip")
|
||||||
|
.current_dir(&container_dir)
|
||||||
|
.arg(&zip_path)
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
.status;
|
||||||
|
if !unzip_status.success() {
|
||||||
|
Err(anyhow!("failed to unzip clangd archive"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: binary_path,
|
||||||
|
arguments: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir)
|
||||||
|
.await
|
||||||
|
.map(|mut binary| {
|
||||||
|
binary.arguments = vec!["--help".into()];
|
||||||
|
binary
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_completion(
|
||||||
|
&self,
|
||||||
|
completion: &lsp2::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
let label = completion
|
||||||
|
.label
|
||||||
|
.strip_prefix('•')
|
||||||
|
.unwrap_or(&completion.label)
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
match completion.kind {
|
||||||
|
Some(lsp2::CompletionItemKind::FIELD) if completion.detail.is_some() => {
|
||||||
|
let detail = completion.detail.as_ref().unwrap();
|
||||||
|
let text = format!("{} {}", detail, label);
|
||||||
|
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
|
||||||
|
let runs = language.highlight_text(&source, 11..11 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
filter_range: detail.len() + 1..text.len(),
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(lsp2::CompletionItemKind::CONSTANT | lsp2::CompletionItemKind::VARIABLE)
|
||||||
|
if completion.detail.is_some() =>
|
||||||
|
{
|
||||||
|
let detail = completion.detail.as_ref().unwrap();
|
||||||
|
let text = format!("{} {}", detail, label);
|
||||||
|
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
filter_range: detail.len() + 1..text.len(),
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(lsp2::CompletionItemKind::FUNCTION | lsp2::CompletionItemKind::METHOD)
|
||||||
|
if completion.detail.is_some() =>
|
||||||
|
{
|
||||||
|
let detail = completion.detail.as_ref().unwrap();
|
||||||
|
let text = format!("{} {}", detail, label);
|
||||||
|
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
filter_range: detail.len() + 1..text.rfind('(').unwrap_or(text.len()),
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(kind) => {
|
||||||
|
let highlight_name = match kind {
|
||||||
|
lsp2::CompletionItemKind::STRUCT
|
||||||
|
| lsp2::CompletionItemKind::INTERFACE
|
||||||
|
| lsp2::CompletionItemKind::CLASS
|
||||||
|
| lsp2::CompletionItemKind::ENUM => Some("type"),
|
||||||
|
lsp2::CompletionItemKind::ENUM_MEMBER => Some("variant"),
|
||||||
|
lsp2::CompletionItemKind::KEYWORD => Some("keyword"),
|
||||||
|
lsp2::CompletionItemKind::VALUE | lsp2::CompletionItemKind::CONSTANT => {
|
||||||
|
Some("constant")
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(highlight_id) = language
|
||||||
|
.grammar()
|
||||||
|
.and_then(|g| g.highlight_id_for_name(highlight_name?))
|
||||||
|
{
|
||||||
|
let mut label = CodeLabel::plain(label.to_string(), None);
|
||||||
|
label.runs.push((
|
||||||
|
0..label.text.rfind('(').unwrap_or(label.text.len()),
|
||||||
|
highlight_id,
|
||||||
|
));
|
||||||
|
return Some(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Some(CodeLabel::plain(label.to_string(), None))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_symbol(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
kind: lsp2::SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
let (text, filter_range, display_range) = match kind {
|
||||||
|
lsp2::SymbolKind::METHOD | lsp2::SymbolKind::FUNCTION => {
|
||||||
|
let text = format!("void {} () {{}}", name);
|
||||||
|
let filter_range = 0..name.len();
|
||||||
|
let display_range = 5..5 + name.len();
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::STRUCT => {
|
||||||
|
let text = format!("struct {} {{}}", name);
|
||||||
|
let filter_range = 7..7 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::ENUM => {
|
||||||
|
let text = format!("enum {} {{}}", name);
|
||||||
|
let filter_range = 5..5 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::INTERFACE | lsp2::SymbolKind::CLASS => {
|
||||||
|
let text = format!("class {} {{}}", name);
|
||||||
|
let filter_range = 6..6 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::CONSTANT => {
|
||||||
|
let text = format!("const int {} = 0;", name);
|
||||||
|
let filter_range = 10..10 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::MODULE => {
|
||||||
|
let text = format!("namespace {} {{}}", name);
|
||||||
|
let filter_range = 10..10 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::TYPE_PARAMETER => {
|
||||||
|
let text = format!("typename {} {{}};", name);
|
||||||
|
let filter_range = 9..9 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(CodeLabel {
|
||||||
|
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||||
|
text: text[display_range].to_string(),
|
||||||
|
filter_range,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
(|| async move {
|
||||||
|
let mut last_clangd_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_clangd_dir = Some(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let clangd_dir = last_clangd_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||||
|
let clangd_bin = clangd_dir.join("bin/clangd");
|
||||||
|
if clangd_bin.exists() {
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: clangd_bin,
|
||||||
|
arguments: vec![],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"missing clangd binary in directory {:?}",
|
||||||
|
clangd_dir
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use gpui2::{Context, TestAppContext};
|
||||||
|
use language2::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
|
||||||
|
use settings2::SettingsStore;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
#[gpui2::test]
|
||||||
|
async fn test_c_autoindent(cx: &mut TestAppContext) {
|
||||||
|
// cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||||
|
cx.update(|cx| {
|
||||||
|
let test_settings = SettingsStore::test(cx);
|
||||||
|
cx.set_global(test_settings);
|
||||||
|
language2::init(cx);
|
||||||
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
|
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||||
|
s.defaults.tab_size = NonZeroU32::new(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let language = crate::languages::language("c", tree_sitter_c::language(), None).await;
|
||||||
|
|
||||||
|
cx.build_model(|cx| {
|
||||||
|
let mut buffer =
|
||||||
|
Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx);
|
||||||
|
|
||||||
|
// empty function
|
||||||
|
buffer.edit([(0..0, "int main() {}")], None, cx);
|
||||||
|
|
||||||
|
// indent inside braces
|
||||||
|
let ix = buffer.len() - 1;
|
||||||
|
buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::EachLine), cx);
|
||||||
|
assert_eq!(buffer.text(), "int main() {\n \n}");
|
||||||
|
|
||||||
|
// indent body of single-statement if statement
|
||||||
|
let ix = buffer.len() - 2;
|
||||||
|
buffer.edit([(ix..ix, "if (a)\nb;")], Some(AutoindentMode::EachLine), cx);
|
||||||
|
assert_eq!(buffer.text(), "int main() {\n if (a)\n b;\n}");
|
||||||
|
|
||||||
|
// indent inside field expression
|
||||||
|
let ix = buffer.len() - 3;
|
||||||
|
buffer.edit([(ix..ix, "\n.c")], Some(AutoindentMode::EachLine), cx);
|
||||||
|
assert_eq!(buffer.text(), "int main() {\n if (a)\n b\n .c;\n}");
|
||||||
|
|
||||||
|
buffer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
3
crates/zed2/src/languages/c/brackets.scm
Normal file
3
crates/zed2/src/languages/c/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("\"" @open "\"" @close)
|
12
crates/zed2/src/languages/c/config.toml
Normal file
12
crates/zed2/src/languages/c/config.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name = "C"
|
||||||
|
path_suffixes = ["c"]
|
||||||
|
line_comment = "// "
|
||||||
|
autoclose_before = ";:.,=}])>"
|
||||||
|
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, not_in = ["string"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
]
|
43
crates/zed2/src/languages/c/embedding.scm
Normal file
43
crates/zed2/src/languages/c/embedding.scm
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(declaration
|
||||||
|
declarator: [
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name)
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name)))
|
||||||
|
]
|
||||||
|
) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(function_definition
|
||||||
|
declarator: [
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
)
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name)))
|
||||||
|
]
|
||||||
|
) @item
|
||||||
|
)
|
109
crates/zed2/src/languages/c/highlights.scm
Normal file
109
crates/zed2/src/languages/c/highlights.scm
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
[
|
||||||
|
"break"
|
||||||
|
"case"
|
||||||
|
"const"
|
||||||
|
"continue"
|
||||||
|
"default"
|
||||||
|
"do"
|
||||||
|
"else"
|
||||||
|
"enum"
|
||||||
|
"extern"
|
||||||
|
"for"
|
||||||
|
"if"
|
||||||
|
"inline"
|
||||||
|
"return"
|
||||||
|
"sizeof"
|
||||||
|
"static"
|
||||||
|
"struct"
|
||||||
|
"switch"
|
||||||
|
"typedef"
|
||||||
|
"union"
|
||||||
|
"volatile"
|
||||||
|
"while"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
"#define"
|
||||||
|
"#elif"
|
||||||
|
"#else"
|
||||||
|
"#endif"
|
||||||
|
"#if"
|
||||||
|
"#ifdef"
|
||||||
|
"#ifndef"
|
||||||
|
"#include"
|
||||||
|
(preproc_directive)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
"--"
|
||||||
|
"-"
|
||||||
|
"-="
|
||||||
|
"->"
|
||||||
|
"="
|
||||||
|
"!="
|
||||||
|
"*"
|
||||||
|
"&"
|
||||||
|
"&&"
|
||||||
|
"+"
|
||||||
|
"++"
|
||||||
|
"+="
|
||||||
|
"<"
|
||||||
|
"=="
|
||||||
|
">"
|
||||||
|
"||"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
"."
|
||||||
|
";"
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
[
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
(string_literal)
|
||||||
|
(system_lib_string)
|
||||||
|
(char_literal)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
(number_literal) @number
|
||||||
|
|
||||||
|
[
|
||||||
|
(true)
|
||||||
|
(false)
|
||||||
|
(null)
|
||||||
|
] @constant
|
||||||
|
|
||||||
|
(identifier) @variable
|
||||||
|
|
||||||
|
((identifier) @constant
|
||||||
|
(#match? @constant "^_*[A-Z][A-Z\\d_]*$"))
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @function)
|
||||||
|
(call_expression
|
||||||
|
function: (field_expression
|
||||||
|
field: (field_identifier) @function))
|
||||||
|
(function_declarator
|
||||||
|
declarator: (identifier) @function)
|
||||||
|
(preproc_function_def
|
||||||
|
name: (identifier) @function.special)
|
||||||
|
|
||||||
|
(field_identifier) @property
|
||||||
|
(statement_identifier) @label
|
||||||
|
|
||||||
|
[
|
||||||
|
(type_identifier)
|
||||||
|
(primitive_type)
|
||||||
|
(sized_type_specifier)
|
||||||
|
] @type
|
||||||
|
|
9
crates/zed2/src/languages/c/indents.scm
Normal file
9
crates/zed2/src/languages/c/indents.scm
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[
|
||||||
|
(field_expression)
|
||||||
|
(assignment_expression)
|
||||||
|
(if_statement)
|
||||||
|
(for_statement)
|
||||||
|
] @indent
|
||||||
|
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
7
crates/zed2/src/languages/c/injections.scm
Normal file
7
crates/zed2/src/languages/c/injections.scm
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
(preproc_def
|
||||||
|
value: (preproc_arg) @content
|
||||||
|
(#set! "language" "c"))
|
||||||
|
|
||||||
|
(preproc_function_def
|
||||||
|
value: (preproc_arg) @content
|
||||||
|
(#set! "language" "c"))
|
70
crates/zed2/src/languages/c/outline.scm
Normal file
70
crates/zed2/src/languages/c/outline.scm
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
(preproc_def
|
||||||
|
"#define" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(preproc_function_def
|
||||||
|
"#define" @context
|
||||||
|
name: (_) @name
|
||||||
|
parameters: (preproc_params
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(type_definition
|
||||||
|
"typedef" @context
|
||||||
|
declarator: (_) @name) @item
|
||||||
|
|
||||||
|
(declaration
|
||||||
|
(type_qualifier)? @context
|
||||||
|
type: (_)? @context
|
||||||
|
declarator: [
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))))
|
||||||
|
]
|
||||||
|
) @item
|
||||||
|
|
||||||
|
(function_definition
|
||||||
|
(type_qualifier)? @context
|
||||||
|
type: (_)? @context
|
||||||
|
declarator: [
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))))
|
||||||
|
]
|
||||||
|
) @item
|
2
crates/zed2/src/languages/c/overrides.scm
Normal file
2
crates/zed2/src/languages/c/overrides.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(comment) @comment
|
||||||
|
(string_literal) @string
|
3
crates/zed2/src/languages/cpp/brackets.scm
Normal file
3
crates/zed2/src/languages/cpp/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("\"" @open "\"" @close)
|
12
crates/zed2/src/languages/cpp/config.toml
Normal file
12
crates/zed2/src/languages/cpp/config.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name = "C++"
|
||||||
|
path_suffixes = ["cc", "cpp", "h", "hpp", "cxx", "hxx", "inl"]
|
||||||
|
line_comment = "// "
|
||||||
|
autoclose_before = ";:.,=}])>"
|
||||||
|
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, not_in = ["string"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
]
|
61
crates/zed2/src/languages/cpp/embedding.scm
Normal file
61
crates/zed2/src/languages/cpp/embedding.scm
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(function_definition
|
||||||
|
(type_qualifier)? @name
|
||||||
|
type: (_)? @name
|
||||||
|
declarator: [
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name)
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @name
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name)))
|
||||||
|
(reference_declarator
|
||||||
|
["&" "&&"] @name
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name))
|
||||||
|
]
|
||||||
|
(type_qualifier)? @name) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(template_declaration
|
||||||
|
(class_specifier
|
||||||
|
"class" @name
|
||||||
|
name: (_) @name)
|
||||||
|
) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(class_specifier
|
||||||
|
"class" @name
|
||||||
|
name: (_) @name) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(enum_specifier
|
||||||
|
"enum" @name
|
||||||
|
name: (_) @name) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(declaration
|
||||||
|
type: (struct_specifier
|
||||||
|
"struct" @name)
|
||||||
|
declarator: (_) @name) @item
|
||||||
|
)
|
158
crates/zed2/src/languages/cpp/highlights.scm
Normal file
158
crates/zed2/src/languages/cpp/highlights.scm
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
(identifier) @variable
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (qualified_identifier
|
||||||
|
name: (identifier) @function))
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @function)
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (field_expression
|
||||||
|
field: (field_identifier) @function))
|
||||||
|
|
||||||
|
(preproc_function_def
|
||||||
|
name: (identifier) @function.special)
|
||||||
|
|
||||||
|
(template_function
|
||||||
|
name: (identifier) @function)
|
||||||
|
|
||||||
|
(template_method
|
||||||
|
name: (field_identifier) @function)
|
||||||
|
|
||||||
|
(function_declarator
|
||||||
|
declarator: (identifier) @function)
|
||||||
|
|
||||||
|
(function_declarator
|
||||||
|
declarator: (qualified_identifier
|
||||||
|
name: (identifier) @function))
|
||||||
|
|
||||||
|
(function_declarator
|
||||||
|
declarator: (field_identifier) @function)
|
||||||
|
|
||||||
|
((namespace_identifier) @type
|
||||||
|
(#match? @type "^[A-Z]"))
|
||||||
|
|
||||||
|
(auto) @type
|
||||||
|
(type_identifier) @type
|
||||||
|
|
||||||
|
((identifier) @constant
|
||||||
|
(#match? @constant "^_*[A-Z][A-Z\\d_]*$"))
|
||||||
|
|
||||||
|
(field_identifier) @property
|
||||||
|
(statement_identifier) @label
|
||||||
|
(this) @variable.special
|
||||||
|
|
||||||
|
[
|
||||||
|
"break"
|
||||||
|
"case"
|
||||||
|
"catch"
|
||||||
|
"class"
|
||||||
|
"co_await"
|
||||||
|
"co_return"
|
||||||
|
"co_yield"
|
||||||
|
"const"
|
||||||
|
"constexpr"
|
||||||
|
"continue"
|
||||||
|
"default"
|
||||||
|
"delete"
|
||||||
|
"do"
|
||||||
|
"else"
|
||||||
|
"enum"
|
||||||
|
"explicit"
|
||||||
|
"extern"
|
||||||
|
"final"
|
||||||
|
"for"
|
||||||
|
"friend"
|
||||||
|
"if"
|
||||||
|
"if"
|
||||||
|
"inline"
|
||||||
|
"mutable"
|
||||||
|
"namespace"
|
||||||
|
"new"
|
||||||
|
"noexcept"
|
||||||
|
"override"
|
||||||
|
"private"
|
||||||
|
"protected"
|
||||||
|
"public"
|
||||||
|
"return"
|
||||||
|
"sizeof"
|
||||||
|
"static"
|
||||||
|
"struct"
|
||||||
|
"switch"
|
||||||
|
"template"
|
||||||
|
"throw"
|
||||||
|
"try"
|
||||||
|
"typedef"
|
||||||
|
"typename"
|
||||||
|
"union"
|
||||||
|
"using"
|
||||||
|
"virtual"
|
||||||
|
"volatile"
|
||||||
|
"while"
|
||||||
|
(primitive_type)
|
||||||
|
(type_qualifier)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
"#define"
|
||||||
|
"#elif"
|
||||||
|
"#else"
|
||||||
|
"#endif"
|
||||||
|
"#if"
|
||||||
|
"#ifdef"
|
||||||
|
"#ifndef"
|
||||||
|
"#include"
|
||||||
|
(preproc_directive)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
(true)
|
||||||
|
(false)
|
||||||
|
(null)
|
||||||
|
(nullptr)
|
||||||
|
] @constant
|
||||||
|
|
||||||
|
(number_literal) @number
|
||||||
|
|
||||||
|
[
|
||||||
|
(string_literal)
|
||||||
|
(system_lib_string)
|
||||||
|
(char_literal)
|
||||||
|
(raw_string_literal)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
[
|
||||||
|
"."
|
||||||
|
";"
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
[
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
"--"
|
||||||
|
"-"
|
||||||
|
"-="
|
||||||
|
"->"
|
||||||
|
"="
|
||||||
|
"!="
|
||||||
|
"*"
|
||||||
|
"&"
|
||||||
|
"&&"
|
||||||
|
"+"
|
||||||
|
"++"
|
||||||
|
"+="
|
||||||
|
"<"
|
||||||
|
"=="
|
||||||
|
">"
|
||||||
|
"||"
|
||||||
|
] @operator
|
7
crates/zed2/src/languages/cpp/indents.scm
Normal file
7
crates/zed2/src/languages/cpp/indents.scm
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[
|
||||||
|
(field_expression)
|
||||||
|
(assignment_expression)
|
||||||
|
] @indent
|
||||||
|
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
7
crates/zed2/src/languages/cpp/injections.scm
Normal file
7
crates/zed2/src/languages/cpp/injections.scm
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
(preproc_def
|
||||||
|
value: (preproc_arg) @content
|
||||||
|
(#set! "language" "c++"))
|
||||||
|
|
||||||
|
(preproc_function_def
|
||||||
|
value: (preproc_arg) @content
|
||||||
|
(#set! "language" "c++"))
|
149
crates/zed2/src/languages/cpp/outline.scm
Normal file
149
crates/zed2/src/languages/cpp/outline.scm
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
(preproc_def
|
||||||
|
"#define" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(preproc_function_def
|
||||||
|
"#define" @context
|
||||||
|
name: (_) @name
|
||||||
|
parameters: (preproc_params
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(type_definition
|
||||||
|
"typedef" @context
|
||||||
|
declarator: (_) @name) @item
|
||||||
|
|
||||||
|
(struct_specifier
|
||||||
|
"struct" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(class_specifier
|
||||||
|
"class" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(enum_specifier
|
||||||
|
"enum" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(enumerator
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(declaration
|
||||||
|
(storage_class_specifier) @context
|
||||||
|
(type_qualifier)? @context
|
||||||
|
type: (_) @context
|
||||||
|
declarator: (init_declarator
|
||||||
|
declarator: (_) @name)) @item
|
||||||
|
|
||||||
|
(function_definition
|
||||||
|
(type_qualifier)? @context
|
||||||
|
type: (_)? @context
|
||||||
|
declarator: [
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))))
|
||||||
|
(reference_declarator
|
||||||
|
["&" "&&"] @context
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
]
|
||||||
|
(type_qualifier)? @context) @item
|
||||||
|
|
||||||
|
(declaration
|
||||||
|
(type_qualifier)? @context
|
||||||
|
type: (_)? @context
|
||||||
|
declarator: [
|
||||||
|
(field_identifier) @name
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (field_identifier) @name)
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))))
|
||||||
|
(reference_declarator
|
||||||
|
["&" "&&"] @context
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
]
|
||||||
|
(type_qualifier)? @context) @item
|
||||||
|
|
||||||
|
(field_declaration
|
||||||
|
(type_qualifier)? @context
|
||||||
|
type: (_) @context
|
||||||
|
declarator: [
|
||||||
|
(field_identifier) @name
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (field_identifier) @name)
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
(pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (pointer_declarator
|
||||||
|
"*" @context
|
||||||
|
declarator: (function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context))))
|
||||||
|
(reference_declarator
|
||||||
|
["&" "&&"] @context
|
||||||
|
(function_declarator
|
||||||
|
declarator: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)))
|
||||||
|
]
|
||||||
|
(type_qualifier)? @context) @item
|
2
crates/zed2/src/languages/cpp/overrides.scm
Normal file
2
crates/zed2/src/languages/cpp/overrides.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(comment) @comment
|
||||||
|
(string_literal) @string
|
130
crates/zed2/src/languages/css.rs
Normal file
130
crates/zed2/src/languages/css.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
|
use lsp2::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::ResultExt;
|
||||||
|
|
||||||
|
const SERVER_PATH: &'static str =
|
||||||
|
"node_modules/vscode-langservers-extracted/bin/vscode-css-language-server";
|
||||||
|
|
||||||
|
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||||
|
vec![server_path.into(), "--stdio".into()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CssLspAdapter {
|
||||||
|
node: Arc<dyn NodeRuntime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CssLspAdapter {
|
||||||
|
pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
|
||||||
|
CssLspAdapter { node }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for CssLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("vscode-css-language-server".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"css"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||||
|
Ok(Box::new(
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version("vscode-langservers-extracted")
|
||||||
|
.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,
|
||||||
|
&[("vscode-langservers-extracted", 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
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||||
|
Some(json!({
|
||||||
|
"provideFormatter": true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(
|
||||||
|
container_dir: PathBuf,
|
||||||
|
node: &dyn NodeRuntime,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
(|| async move {
|
||||||
|
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/zed2/src/languages/css/brackets.scm
Normal file
3
crates/zed2/src/languages/css/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("(" @open ")" @close)
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
13
crates/zed2/src/languages/css/config.toml
Normal file
13
crates/zed2/src/languages/css/config.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name = "CSS"
|
||||||
|
path_suffixes = ["css"]
|
||||||
|
autoclose_before = ";:.,=}])>"
|
||||||
|
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, not_in = ["string", "comment"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
]
|
||||||
|
word_characters = ["-"]
|
||||||
|
block_comment = ["/* ", " */"]
|
||||||
|
prettier_parser_name = "css"
|
78
crates/zed2/src/languages/css/highlights.scm
Normal file
78
crates/zed2/src/languages/css/highlights.scm
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
(tag_name)
|
||||||
|
(nesting_selector)
|
||||||
|
(universal_selector)
|
||||||
|
] @tag
|
||||||
|
|
||||||
|
[
|
||||||
|
"~"
|
||||||
|
">"
|
||||||
|
"+"
|
||||||
|
"-"
|
||||||
|
"*"
|
||||||
|
"/"
|
||||||
|
"="
|
||||||
|
"^="
|
||||||
|
"|="
|
||||||
|
"~="
|
||||||
|
"$="
|
||||||
|
"*="
|
||||||
|
"and"
|
||||||
|
"or"
|
||||||
|
"not"
|
||||||
|
"only"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
(attribute_selector (plain_value) @string)
|
||||||
|
|
||||||
|
(attribute_name) @attribute
|
||||||
|
(pseudo_element_selector (tag_name) @attribute)
|
||||||
|
(pseudo_class_selector (class_name) @attribute)
|
||||||
|
|
||||||
|
[
|
||||||
|
(class_name)
|
||||||
|
(id_name)
|
||||||
|
(namespace_name)
|
||||||
|
(property_name)
|
||||||
|
(feature_name)
|
||||||
|
] @property
|
||||||
|
|
||||||
|
(function_name) @function
|
||||||
|
|
||||||
|
(
|
||||||
|
[
|
||||||
|
(property_name)
|
||||||
|
(plain_value)
|
||||||
|
] @variable.special
|
||||||
|
(#match? @variable.special "^--")
|
||||||
|
)
|
||||||
|
|
||||||
|
[
|
||||||
|
"@media"
|
||||||
|
"@import"
|
||||||
|
"@charset"
|
||||||
|
"@namespace"
|
||||||
|
"@supports"
|
||||||
|
"@keyframes"
|
||||||
|
(at_keyword)
|
||||||
|
(to)
|
||||||
|
(from)
|
||||||
|
(important)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(string_value) @string
|
||||||
|
(color_value) @string.special
|
||||||
|
|
||||||
|
[
|
||||||
|
(integer_value)
|
||||||
|
(float_value)
|
||||||
|
] @number
|
||||||
|
|
||||||
|
(unit) @type
|
||||||
|
|
||||||
|
[
|
||||||
|
","
|
||||||
|
":"
|
||||||
|
] @punctuation.delimiter
|
1
crates/zed2/src/languages/css/indents.scm
Normal file
1
crates/zed2/src/languages/css/indents.scm
Normal file
|
@ -0,0 +1 @@
|
||||||
|
(_ "{" "}" @end) @indent
|
2
crates/zed2/src/languages/css/overrides.scm
Normal file
2
crates/zed2/src/languages/css/overrides.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(comment) @comment
|
||||||
|
(string_value) @string
|
546
crates/zed2/src/languages/elixir.rs
Normal file
546
crates/zed2/src/languages/elixir.rs
Normal file
|
@ -0,0 +1,546 @@
|
||||||
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use gpui2::{AsyncAppContext, Task};
|
||||||
|
pub use language2::*;
|
||||||
|
use lsp2::{CompletionItemKind, LanguageServerBinary, SymbolKind};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use settings2::Settings;
|
||||||
|
use smol::fs::{self, File};
|
||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
env::consts,
|
||||||
|
ops::Deref,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering::SeqCst},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use util::{
|
||||||
|
async_maybe,
|
||||||
|
fs::remove_matching,
|
||||||
|
github::{latest_github_release, GitHubLspBinaryVersion},
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct ElixirSettings {
|
||||||
|
pub lsp: ElixirLspSetting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ElixirLspSetting {
|
||||||
|
ElixirLs,
|
||||||
|
NextLs,
|
||||||
|
Local {
|
||||||
|
path: String,
|
||||||
|
arguments: Vec<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Default, Deserialize, JsonSchema)]
|
||||||
|
pub struct ElixirSettingsContent {
|
||||||
|
lsp: Option<ElixirLspSetting>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings for ElixirSettings {
|
||||||
|
const KEY: Option<&'static str> = Some("elixir");
|
||||||
|
|
||||||
|
type FileContent = ElixirSettingsContent;
|
||||||
|
|
||||||
|
fn load(
|
||||||
|
default_value: &Self::FileContent,
|
||||||
|
user_values: &[&Self::FileContent],
|
||||||
|
_: &mut gpui2::AppContext,
|
||||||
|
) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Self::load_via_json_merge(default_value, user_values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ElixirLspAdapter;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for ElixirLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("elixir-ls".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"elixir-ls"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn will_start_server(
|
||||||
|
&self,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Option<Task<Result<()>>> {
|
||||||
|
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
const NOTIFICATION_MESSAGE: &str = "Could not run the elixir language server, `elixir-ls`, because `elixir` was not found.";
|
||||||
|
|
||||||
|
let delegate = delegate.clone();
|
||||||
|
Some(cx.spawn(|cx| async move {
|
||||||
|
let elixir_output = smol::process::Command::new("elixir")
|
||||||
|
.args(["--version"])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
if elixir_output.is_err() {
|
||||||
|
if DID_SHOW_NOTIFICATION
|
||||||
|
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
cx.update(|cx| {
|
||||||
|
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
return Err(anyhow!("cannot run elixir-ls"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
let http = delegate.http_client();
|
||||||
|
let release = latest_github_release("elixir-lsp/elixir-ls", false, http).await?;
|
||||||
|
let version_name = release
|
||||||
|
.name
|
||||||
|
.strip_prefix("Release ")
|
||||||
|
.context("Elixir-ls release name does not start with prefix")?
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let asset_name = format!("elixir-ls-{}.zip", &version_name);
|
||||||
|
let asset = release
|
||||||
|
.assets
|
||||||
|
.iter()
|
||||||
|
.find(|asset| asset.name == asset_name)
|
||||||
|
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||||
|
|
||||||
|
let version = GitHubLspBinaryVersion {
|
||||||
|
name: version_name,
|
||||||
|
url: asset.browser_download_url.clone(),
|
||||||
|
};
|
||||||
|
Ok(Box::new(version) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||||
|
let zip_path = container_dir.join(format!("elixir-ls_{}.zip", version.name));
|
||||||
|
let version_dir = container_dir.join(format!("elixir-ls_{}", version.name));
|
||||||
|
let binary_path = version_dir.join("language_server.sh");
|
||||||
|
|
||||||
|
if fs::metadata(&binary_path).await.is_err() {
|
||||||
|
let mut response = delegate
|
||||||
|
.http_client()
|
||||||
|
.get(&version.url, Default::default(), true)
|
||||||
|
.await
|
||||||
|
.context("error downloading release")?;
|
||||||
|
let mut file = File::create(&zip_path)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("failed to create file {}", zip_path.display()))?;
|
||||||
|
if !response.status().is_success() {
|
||||||
|
Err(anyhow!(
|
||||||
|
"download failed with status {}",
|
||||||
|
response.status().to_string()
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
futures::io::copy(response.body_mut(), &mut file).await?;
|
||||||
|
|
||||||
|
fs::create_dir_all(&version_dir)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("failed to create directory {}", version_dir.display()))?;
|
||||||
|
let unzip_status = smol::process::Command::new("unzip")
|
||||||
|
.arg(&zip_path)
|
||||||
|
.arg("-d")
|
||||||
|
.arg(&version_dir)
|
||||||
|
.output()
|
||||||
|
.await?
|
||||||
|
.status;
|
||||||
|
if !unzip_status.success() {
|
||||||
|
Err(anyhow!("failed to unzip elixir-ls archive"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: binary_path,
|
||||||
|
arguments: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary_elixir_ls(container_dir).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary_elixir_ls(container_dir).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_completion(
|
||||||
|
&self,
|
||||||
|
completion: &lsp2::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
match completion.kind.zip(completion.detail.as_ref()) {
|
||||||
|
Some((_, detail)) if detail.starts_with("(function)") => {
|
||||||
|
let text = detail.strip_prefix("(function) ")?;
|
||||||
|
let filter_range = 0..text.find('(').unwrap_or(text.len());
|
||||||
|
let source = Rope::from(format!("def {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 4..4 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text: text.to_string(),
|
||||||
|
runs,
|
||||||
|
filter_range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((_, detail)) if detail.starts_with("(macro)") => {
|
||||||
|
let text = detail.strip_prefix("(macro) ")?;
|
||||||
|
let filter_range = 0..text.find('(').unwrap_or(text.len());
|
||||||
|
let source = Rope::from(format!("defmacro {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 9..9 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text: text.to_string(),
|
||||||
|
runs,
|
||||||
|
filter_range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
CompletionItemKind::CLASS
|
||||||
|
| CompletionItemKind::MODULE
|
||||||
|
| CompletionItemKind::INTERFACE
|
||||||
|
| CompletionItemKind::STRUCT,
|
||||||
|
_,
|
||||||
|
)) => {
|
||||||
|
let filter_range = 0..completion
|
||||||
|
.label
|
||||||
|
.find(" (")
|
||||||
|
.unwrap_or(completion.label.len());
|
||||||
|
let text = &completion.label[filter_range.clone()];
|
||||||
|
let source = Rope::from(format!("defmodule {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 10..10 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text: completion.label.clone(),
|
||||||
|
runs,
|
||||||
|
filter_range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_symbol(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
kind: SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
let (text, filter_range, display_range) = match kind {
|
||||||
|
SymbolKind::METHOD | SymbolKind::FUNCTION => {
|
||||||
|
let text = format!("def {}", name);
|
||||||
|
let filter_range = 4..4 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
SymbolKind::CLASS | SymbolKind::MODULE | SymbolKind::INTERFACE | SymbolKind::STRUCT => {
|
||||||
|
let text = format!("defmodule {}", name);
|
||||||
|
let filter_range = 10..10 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(CodeLabel {
|
||||||
|
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||||
|
text: text[display_range].to_string(),
|
||||||
|
filter_range,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary_elixir_ls(
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
(|| async move {
|
||||||
|
let mut last = None;
|
||||||
|
let mut entries = fs::read_dir(&container_dir).await?;
|
||||||
|
while let Some(entry) = entries.next().await {
|
||||||
|
last = Some(entry?.path());
|
||||||
|
}
|
||||||
|
last.map(|path| LanguageServerBinary {
|
||||||
|
path,
|
||||||
|
arguments: vec![],
|
||||||
|
})
|
||||||
|
.ok_or_else(|| anyhow!("no cached binary"))
|
||||||
|
})()
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NextLspAdapter;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for NextLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("next-ls".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"next-ls"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
let release =
|
||||||
|
latest_github_release("elixir-tools/next-ls", false, delegate.http_client()).await?;
|
||||||
|
let version = release.name.clone();
|
||||||
|
let platform = match consts::ARCH {
|
||||||
|
"x86_64" => "darwin_amd64",
|
||||||
|
"aarch64" => "darwin_arm64",
|
||||||
|
other => bail!("Running on unsupported platform: {other}"),
|
||||||
|
};
|
||||||
|
let asset_name = format!("next_ls_{}", platform);
|
||||||
|
let asset = release
|
||||||
|
.assets
|
||||||
|
.iter()
|
||||||
|
.find(|asset| asset.name == asset_name)
|
||||||
|
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||||
|
let version = GitHubLspBinaryVersion {
|
||||||
|
name: version,
|
||||||
|
url: asset.browser_download_url.clone(),
|
||||||
|
};
|
||||||
|
Ok(Box::new(version) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||||
|
|
||||||
|
let binary_path = container_dir.join("next-ls");
|
||||||
|
|
||||||
|
if fs::metadata(&binary_path).await.is_err() {
|
||||||
|
let mut response = delegate
|
||||||
|
.http_client()
|
||||||
|
.get(&version.url, Default::default(), true)
|
||||||
|
.await
|
||||||
|
.map_err(|err| anyhow!("error downloading release: {}", err))?;
|
||||||
|
|
||||||
|
let mut file = smol::fs::File::create(&binary_path).await?;
|
||||||
|
if !response.status().is_success() {
|
||||||
|
Err(anyhow!(
|
||||||
|
"download failed with status {}",
|
||||||
|
response.status().to_string()
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
futures::io::copy(response.body_mut(), &mut file).await?;
|
||||||
|
|
||||||
|
fs::set_permissions(
|
||||||
|
&binary_path,
|
||||||
|
<fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: binary_path,
|
||||||
|
arguments: vec!["--stdio".into()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary_next(container_dir)
|
||||||
|
.await
|
||||||
|
.map(|mut binary| {
|
||||||
|
binary.arguments = vec!["--stdio".into()];
|
||||||
|
binary
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary_next(container_dir)
|
||||||
|
.await
|
||||||
|
.map(|mut binary| {
|
||||||
|
binary.arguments = vec!["--help".into()];
|
||||||
|
binary
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_completion(
|
||||||
|
&self,
|
||||||
|
completion: &lsp2::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
label_for_completion_elixir(completion, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_symbol(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
symbol_kind: SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
label_for_symbol_elixir(name, symbol_kind, language)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary_next(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
async_maybe!({
|
||||||
|
let mut last_binary_path = 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_file()
|
||||||
|
&& entry
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.map_or(false, |name| name == "next-ls")
|
||||||
|
{
|
||||||
|
last_binary_path = Some(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(path) = last_binary_path {
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path,
|
||||||
|
arguments: Vec::new(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("no cached binary"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalLspAdapter {
|
||||||
|
pub path: String,
|
||||||
|
pub arguments: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for LocalLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("local-ls".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"local-ls"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
Ok(Box::new(()) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
_: Box<dyn 'static + Send + Any>,
|
||||||
|
_: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let path = shellexpand::full(&self.path)?;
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: PathBuf::from(path.deref()),
|
||||||
|
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
_: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
let path = shellexpand::full(&self.path).ok()?;
|
||||||
|
Some(LanguageServerBinary {
|
||||||
|
path: PathBuf::from(path.deref()),
|
||||||
|
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
let path = shellexpand::full(&self.path).ok()?;
|
||||||
|
Some(LanguageServerBinary {
|
||||||
|
path: PathBuf::from(path.deref()),
|
||||||
|
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_completion(
|
||||||
|
&self,
|
||||||
|
completion: &lsp2::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
label_for_completion_elixir(completion, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_symbol(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
symbol: SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
label_for_symbol_elixir(name, symbol, language)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_for_completion_elixir(
|
||||||
|
completion: &lsp2::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
return Some(CodeLabel {
|
||||||
|
runs: language.highlight_text(&completion.label.clone().into(), 0..completion.label.len()),
|
||||||
|
text: completion.label.clone(),
|
||||||
|
filter_range: 0..completion.label.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_for_symbol_elixir(
|
||||||
|
name: &str,
|
||||||
|
_: SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
Some(CodeLabel {
|
||||||
|
runs: language.highlight_text(&name.into(), 0..name.len()),
|
||||||
|
text: name.to_string(),
|
||||||
|
filter_range: 0..name.len(),
|
||||||
|
})
|
||||||
|
}
|
5
crates/zed2/src/languages/elixir/brackets.scm
Normal file
5
crates/zed2/src/languages/elixir/brackets.scm
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
("(" @open ")" @close)
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("\"" @open "\"" @close)
|
||||||
|
("do" @open "end" @close)
|
16
crates/zed2/src/languages/elixir/config.toml
Normal file
16
crates/zed2/src/languages/elixir/config.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name = "Elixir"
|
||||||
|
path_suffixes = ["ex", "exs"]
|
||||||
|
line_comment = "# "
|
||||||
|
autoclose_before = ";:.,=}])>"
|
||||||
|
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, not_in = ["string", "comment"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
]
|
||||||
|
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||||
|
|
||||||
|
[overrides.string]
|
||||||
|
word_characters = ["-"]
|
||||||
|
opt_into_language_servers = ["tailwindcss-language-server"]
|
27
crates/zed2/src/languages/elixir/embedding.scm
Normal file
27
crates/zed2/src/languages/elixir/embedding.scm
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
(
|
||||||
|
(unary_operator
|
||||||
|
operator: "@"
|
||||||
|
operand: (call
|
||||||
|
target: (identifier) @unary
|
||||||
|
(#match? @unary "^(doc)$"))
|
||||||
|
) @context
|
||||||
|
.
|
||||||
|
(call
|
||||||
|
target: (identifier) @name
|
||||||
|
(arguments
|
||||||
|
[
|
||||||
|
(identifier) @name
|
||||||
|
(call
|
||||||
|
target: (identifier) @name)
|
||||||
|
(binary_operator
|
||||||
|
left: (call
|
||||||
|
target: (identifier) @name)
|
||||||
|
operator: "when")
|
||||||
|
])
|
||||||
|
(#match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: (identifier) @name
|
||||||
|
(arguments (alias) @name)
|
||||||
|
(#match? @name "^(defmodule|defprotocol)$")) @item
|
153
crates/zed2/src/languages/elixir/highlights.scm
Normal file
153
crates/zed2/src/languages/elixir/highlights.scm
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword
|
||||||
|
|
||||||
|
(unary_operator
|
||||||
|
operator: "&"
|
||||||
|
operand: (integer) @operator)
|
||||||
|
|
||||||
|
(operator_identifier) @operator
|
||||||
|
|
||||||
|
(unary_operator
|
||||||
|
operator: _ @operator)
|
||||||
|
|
||||||
|
(binary_operator
|
||||||
|
operator: _ @operator)
|
||||||
|
|
||||||
|
(dot
|
||||||
|
operator: _ @operator)
|
||||||
|
|
||||||
|
(stab_clause
|
||||||
|
operator: _ @operator)
|
||||||
|
|
||||||
|
[
|
||||||
|
(boolean)
|
||||||
|
(nil)
|
||||||
|
] @constant
|
||||||
|
|
||||||
|
[
|
||||||
|
(integer)
|
||||||
|
(float)
|
||||||
|
] @number
|
||||||
|
|
||||||
|
(alias) @type
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: (dot
|
||||||
|
left: (atom) @type))
|
||||||
|
|
||||||
|
(char) @constant
|
||||||
|
|
||||||
|
(escape_sequence) @string.escape
|
||||||
|
|
||||||
|
[
|
||||||
|
(atom)
|
||||||
|
(quoted_atom)
|
||||||
|
(keyword)
|
||||||
|
(quoted_keyword)
|
||||||
|
] @string.special.symbol
|
||||||
|
|
||||||
|
[
|
||||||
|
(string)
|
||||||
|
(charlist)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
(sigil
|
||||||
|
(sigil_name) @__name__
|
||||||
|
quoted_start: _ @string
|
||||||
|
quoted_end: _ @string
|
||||||
|
(#match? @__name__ "^[sS]$")) @string
|
||||||
|
|
||||||
|
(sigil
|
||||||
|
(sigil_name) @__name__
|
||||||
|
quoted_start: _ @string.regex
|
||||||
|
quoted_end: _ @string.regex
|
||||||
|
(#match? @__name__ "^[rR]$")) @string.regex
|
||||||
|
|
||||||
|
(sigil
|
||||||
|
(sigil_name) @__name__
|
||||||
|
quoted_start: _ @string.special
|
||||||
|
quoted_end: _ @string.special) @string.special
|
||||||
|
|
||||||
|
(
|
||||||
|
(identifier) @comment.unused
|
||||||
|
(#match? @comment.unused "^_")
|
||||||
|
)
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: [
|
||||||
|
(identifier) @function
|
||||||
|
(dot
|
||||||
|
right: (identifier) @function)
|
||||||
|
])
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: (identifier) @keyword
|
||||||
|
(arguments
|
||||||
|
[
|
||||||
|
(identifier) @function
|
||||||
|
(binary_operator
|
||||||
|
left: (identifier) @function
|
||||||
|
operator: "when")
|
||||||
|
(binary_operator
|
||||||
|
operator: "|>"
|
||||||
|
right: (identifier))
|
||||||
|
])
|
||||||
|
(#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$"))
|
||||||
|
|
||||||
|
(binary_operator
|
||||||
|
operator: "|>"
|
||||||
|
right: (identifier) @function)
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: (identifier) @keyword
|
||||||
|
(#match? @keyword "^(def|defdelegate|defexception|defguard|defguardp|defimpl|defmacro|defmacrop|defmodule|defn|defnp|defoverridable|defp|defprotocol|defstruct)$"))
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: (identifier) @keyword
|
||||||
|
(#match? @keyword "^(alias|case|cond|else|for|if|import|quote|raise|receive|require|reraise|super|throw|try|unless|unquote|unquote_splicing|use|with)$"))
|
||||||
|
|
||||||
|
(
|
||||||
|
(identifier) @constant.builtin
|
||||||
|
(#match? @constant.builtin "^(__MODULE__|__DIR__|__ENV__|__CALLER__|__STACKTRACE__)$")
|
||||||
|
)
|
||||||
|
|
||||||
|
(unary_operator
|
||||||
|
operator: "@" @comment.doc
|
||||||
|
operand: (call
|
||||||
|
target: (identifier) @__attribute__ @comment.doc
|
||||||
|
(arguments
|
||||||
|
[
|
||||||
|
(string)
|
||||||
|
(charlist)
|
||||||
|
(sigil)
|
||||||
|
(boolean)
|
||||||
|
] @comment.doc))
|
||||||
|
(#match? @__attribute__ "^(moduledoc|typedoc|doc)$"))
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
"%"
|
||||||
|
] @punctuation
|
||||||
|
|
||||||
|
[
|
||||||
|
","
|
||||||
|
";"
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
"<<"
|
||||||
|
">>"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded
|
||||||
|
|
||||||
|
((sigil
|
||||||
|
(sigil_name) @_sigil_name
|
||||||
|
(quoted_content) @embedded)
|
||||||
|
(#eq? @_sigil_name "H"))
|
6
crates/zed2/src/languages/elixir/indents.scm
Normal file
6
crates/zed2/src/languages/elixir/indents.scm
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
(call) @indent
|
||||||
|
|
||||||
|
(_ "[" "]" @end) @indent
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
||||||
|
(_ "do" "end" @end) @indent
|
7
crates/zed2/src/languages/elixir/injections.scm
Normal file
7
crates/zed2/src/languages/elixir/injections.scm
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
; Phoenix HTML template
|
||||||
|
|
||||||
|
((sigil
|
||||||
|
(sigil_name) @_sigil_name
|
||||||
|
(quoted_content) @content)
|
||||||
|
(#eq? @_sigil_name "H")
|
||||||
|
(#set! language "heex"))
|
26
crates/zed2/src/languages/elixir/outline.scm
Normal file
26
crates/zed2/src/languages/elixir/outline.scm
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
(call
|
||||||
|
target: (identifier) @context
|
||||||
|
(arguments (alias) @name)
|
||||||
|
(#match? @context "^(defmodule|defprotocol)$")) @item
|
||||||
|
|
||||||
|
(call
|
||||||
|
target: (identifier) @context
|
||||||
|
(arguments
|
||||||
|
[
|
||||||
|
(identifier) @name
|
||||||
|
(call
|
||||||
|
target: (identifier) @name
|
||||||
|
(arguments
|
||||||
|
"(" @context.extra
|
||||||
|
_* @context.extra
|
||||||
|
")" @context.extra))
|
||||||
|
(binary_operator
|
||||||
|
left: (call
|
||||||
|
target: (identifier) @name
|
||||||
|
(arguments
|
||||||
|
"(" @context.extra
|
||||||
|
_* @context.extra
|
||||||
|
")" @context.extra))
|
||||||
|
operator: "when")
|
||||||
|
])
|
||||||
|
(#match? @context "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
|
2
crates/zed2/src/languages/elixir/overrides.scm
Normal file
2
crates/zed2/src/languages/elixir/overrides.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(comment) @comment
|
||||||
|
[(string) (charlist)] @string
|
11
crates/zed2/src/languages/elm/config.toml
Normal file
11
crates/zed2/src/languages/elm/config.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
name = "Elm"
|
||||||
|
path_suffixes = ["elm"]
|
||||||
|
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, not_in = ["string"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||||
|
]
|
72
crates/zed2/src/languages/elm/highlights.scm
Normal file
72
crates/zed2/src/languages/elm/highlights.scm
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
[
|
||||||
|
"if"
|
||||||
|
"then"
|
||||||
|
"else"
|
||||||
|
"let"
|
||||||
|
"in"
|
||||||
|
(case)
|
||||||
|
(of)
|
||||||
|
(backslash)
|
||||||
|
(as)
|
||||||
|
(port)
|
||||||
|
(exposing)
|
||||||
|
(alias)
|
||||||
|
(import)
|
||||||
|
(module)
|
||||||
|
(type)
|
||||||
|
(arrow)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
(eq)
|
||||||
|
(operator_identifier)
|
||||||
|
(colon)
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
(type_annotation(lower_case_identifier) @function)
|
||||||
|
(port_annotation(lower_case_identifier) @function)
|
||||||
|
(function_declaration_left(lower_case_identifier) @function.definition)
|
||||||
|
|
||||||
|
(function_call_expr
|
||||||
|
target: (value_expr
|
||||||
|
name: (value_qid (lower_case_identifier) @function)))
|
||||||
|
|
||||||
|
(exposed_value(lower_case_identifier) @function)
|
||||||
|
(exposed_type(upper_case_identifier) @type)
|
||||||
|
|
||||||
|
(field_access_expr(value_expr(value_qid)) @identifier)
|
||||||
|
(lower_pattern) @variable
|
||||||
|
(record_base_identifier) @identifier
|
||||||
|
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
"|"
|
||||||
|
","
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
(number_constant_expr) @constant
|
||||||
|
|
||||||
|
(type_declaration(upper_case_identifier) @type)
|
||||||
|
(type_ref) @type
|
||||||
|
(type_alias_declaration name: (upper_case_identifier) @type)
|
||||||
|
|
||||||
|
(value_expr(upper_case_qid(upper_case_identifier)) @type)
|
||||||
|
|
||||||
|
[
|
||||||
|
(line_comment)
|
||||||
|
(block_comment)
|
||||||
|
] @comment
|
||||||
|
|
||||||
|
(string_escape) @string.escape
|
||||||
|
|
||||||
|
[
|
||||||
|
(open_quote)
|
||||||
|
(close_quote)
|
||||||
|
(regular_string_part)
|
||||||
|
(open_char)
|
||||||
|
(close_char)
|
||||||
|
] @string
|
2
crates/zed2/src/languages/elm/injections.scm
Normal file
2
crates/zed2/src/languages/elm/injections.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
((glsl_content) @content
|
||||||
|
(#set! "language" "glsl"))
|
22
crates/zed2/src/languages/elm/outline.scm
Normal file
22
crates/zed2/src/languages/elm/outline.scm
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
(type_declaration
|
||||||
|
(type) @context
|
||||||
|
(upper_case_identifier) @name) @item
|
||||||
|
|
||||||
|
(type_alias_declaration
|
||||||
|
(type) @context
|
||||||
|
(alias) @context
|
||||||
|
name: (upper_case_identifier) @name) @item
|
||||||
|
|
||||||
|
(type_alias_declaration
|
||||||
|
typeExpression:
|
||||||
|
(type_expression
|
||||||
|
part: (record_type
|
||||||
|
(field_type
|
||||||
|
name: (lower_case_identifier) @name) @item)))
|
||||||
|
|
||||||
|
(union_variant
|
||||||
|
name: (upper_case_identifier) @name) @item
|
||||||
|
|
||||||
|
(value_declaration
|
||||||
|
functionDeclarationLeft:
|
||||||
|
(function_declaration_left(lower_case_identifier) @name)) @item
|
8
crates/zed2/src/languages/erb/config.toml
Normal file
8
crates/zed2/src/languages/erb/config.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
name = "ERB"
|
||||||
|
path_suffixes = ["erb"]
|
||||||
|
autoclose_before = ">})"
|
||||||
|
brackets = [
|
||||||
|
{ start = "<", end = ">", close = true, newline = true },
|
||||||
|
]
|
||||||
|
block_comment = ["<%#", "%>"]
|
||||||
|
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
12
crates/zed2/src/languages/erb/highlights.scm
Normal file
12
crates/zed2/src/languages/erb/highlights.scm
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
(comment_directive) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
"<%#"
|
||||||
|
"<%"
|
||||||
|
"<%="
|
||||||
|
"<%_"
|
||||||
|
"<%-"
|
||||||
|
"%>"
|
||||||
|
"-%>"
|
||||||
|
"_%>"
|
||||||
|
] @keyword
|
7
crates/zed2/src/languages/erb/injections.scm
Normal file
7
crates/zed2/src/languages/erb/injections.scm
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
((code) @content
|
||||||
|
(#set! "language" "ruby")
|
||||||
|
(#set! "combined"))
|
||||||
|
|
||||||
|
((content) @content
|
||||||
|
(#set! "language" "html")
|
||||||
|
(#set! "combined"))
|
9
crates/zed2/src/languages/glsl/config.toml
Normal file
9
crates/zed2/src/languages/glsl/config.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
name = "GLSL"
|
||||||
|
path_suffixes = ["vert", "frag", "tesc", "tese", "geom", "comp"]
|
||||||
|
line_comment = "// "
|
||||||
|
block_comment = ["/* ", " */"]
|
||||||
|
brackets = [
|
||||||
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
|
{ start = "(", end = ")", close = true, newline = true },
|
||||||
|
]
|
118
crates/zed2/src/languages/glsl/highlights.scm
Normal file
118
crates/zed2/src/languages/glsl/highlights.scm
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
"break" @keyword
|
||||||
|
"case" @keyword
|
||||||
|
"const" @keyword
|
||||||
|
"continue" @keyword
|
||||||
|
"default" @keyword
|
||||||
|
"do" @keyword
|
||||||
|
"else" @keyword
|
||||||
|
"enum" @keyword
|
||||||
|
"extern" @keyword
|
||||||
|
"for" @keyword
|
||||||
|
"if" @keyword
|
||||||
|
"inline" @keyword
|
||||||
|
"return" @keyword
|
||||||
|
"sizeof" @keyword
|
||||||
|
"static" @keyword
|
||||||
|
"struct" @keyword
|
||||||
|
"switch" @keyword
|
||||||
|
"typedef" @keyword
|
||||||
|
"union" @keyword
|
||||||
|
"volatile" @keyword
|
||||||
|
"while" @keyword
|
||||||
|
|
||||||
|
"#define" @keyword
|
||||||
|
"#elif" @keyword
|
||||||
|
"#else" @keyword
|
||||||
|
"#endif" @keyword
|
||||||
|
"#if" @keyword
|
||||||
|
"#ifdef" @keyword
|
||||||
|
"#ifndef" @keyword
|
||||||
|
"#include" @keyword
|
||||||
|
(preproc_directive) @keyword
|
||||||
|
|
||||||
|
"--" @operator
|
||||||
|
"-" @operator
|
||||||
|
"-=" @operator
|
||||||
|
"->" @operator
|
||||||
|
"=" @operator
|
||||||
|
"!=" @operator
|
||||||
|
"*" @operator
|
||||||
|
"&" @operator
|
||||||
|
"&&" @operator
|
||||||
|
"+" @operator
|
||||||
|
"++" @operator
|
||||||
|
"+=" @operator
|
||||||
|
"<" @operator
|
||||||
|
"==" @operator
|
||||||
|
">" @operator
|
||||||
|
"||" @operator
|
||||||
|
|
||||||
|
"." @delimiter
|
||||||
|
";" @delimiter
|
||||||
|
|
||||||
|
(string_literal) @string
|
||||||
|
(system_lib_string) @string
|
||||||
|
|
||||||
|
(null) @constant
|
||||||
|
(number_literal) @number
|
||||||
|
(char_literal) @number
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @function)
|
||||||
|
(call_expression
|
||||||
|
function: (field_expression
|
||||||
|
field: (field_identifier) @function))
|
||||||
|
(function_declarator
|
||||||
|
declarator: (identifier) @function)
|
||||||
|
(preproc_function_def
|
||||||
|
name: (identifier) @function.special)
|
||||||
|
|
||||||
|
(field_identifier) @property
|
||||||
|
(statement_identifier) @label
|
||||||
|
(type_identifier) @type
|
||||||
|
(primitive_type) @type
|
||||||
|
(sized_type_specifier) @type
|
||||||
|
|
||||||
|
((identifier) @constant
|
||||||
|
(#match? @constant "^[A-Z][A-Z\\d_]*$"))
|
||||||
|
|
||||||
|
(identifier) @variable
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
; inherits: c
|
||||||
|
|
||||||
|
[
|
||||||
|
"in"
|
||||||
|
"out"
|
||||||
|
"inout"
|
||||||
|
"uniform"
|
||||||
|
"shared"
|
||||||
|
"layout"
|
||||||
|
"attribute"
|
||||||
|
"varying"
|
||||||
|
"buffer"
|
||||||
|
"coherent"
|
||||||
|
"readonly"
|
||||||
|
"writeonly"
|
||||||
|
"precision"
|
||||||
|
"highp"
|
||||||
|
"mediump"
|
||||||
|
"lowp"
|
||||||
|
"centroid"
|
||||||
|
"sample"
|
||||||
|
"patch"
|
||||||
|
"smooth"
|
||||||
|
"flat"
|
||||||
|
"noperspective"
|
||||||
|
"invariant"
|
||||||
|
"precise"
|
||||||
|
] @type.qualifier
|
||||||
|
|
||||||
|
"subroutine" @keyword.function
|
||||||
|
|
||||||
|
(extension_storage_class) @storageclass
|
||||||
|
|
||||||
|
(
|
||||||
|
(identifier) @variable.builtin
|
||||||
|
(#match? @variable.builtin "^gl_")
|
||||||
|
)
|
464
crates/zed2/src/languages/go.rs
Normal file
464
crates/zed2/src/languages/go.rs
Normal file
|
@ -0,0 +1,464 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use gpui2::{AsyncAppContext, Task};
|
||||||
|
pub use language2::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use lsp2::LanguageServerBinary;
|
||||||
|
use regex::Regex;
|
||||||
|
use smol::{fs, process};
|
||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
ffi::{OsStr, OsString},
|
||||||
|
ops::Range,
|
||||||
|
path::PathBuf,
|
||||||
|
str,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering::SeqCst},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use util::{fs::remove_matching, github::latest_github_release, ResultExt};
|
||||||
|
|
||||||
|
fn server_binary_arguments() -> Vec<OsString> {
|
||||||
|
vec!["-mode=stdio".into()]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct GoLspAdapter;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref GOPLS_VERSION_REGEX: Regex = Regex::new(r"\d+\.\d+\.\d+").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl super::LspAdapter for GoLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("gopls".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"gopls"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
let release = latest_github_release("golang/tools", false, delegate.http_client()).await?;
|
||||||
|
let version: Option<String> = release.name.strip_prefix("gopls/v").map(str::to_string);
|
||||||
|
if version.is_none() {
|
||||||
|
log::warn!(
|
||||||
|
"couldn't infer gopls version from github release name '{}'",
|
||||||
|
release.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(Box::new(version) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn will_fetch_server(
|
||||||
|
&self,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Option<Task<Result<()>>> {
|
||||||
|
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
const NOTIFICATION_MESSAGE: &str =
|
||||||
|
"Could not install the Go language server `gopls`, because `go` was not found.";
|
||||||
|
|
||||||
|
let delegate = delegate.clone();
|
||||||
|
Some(cx.spawn(|cx| async move {
|
||||||
|
let install_output = process::Command::new("go").args(["version"]).output().await;
|
||||||
|
if install_output.is_err() {
|
||||||
|
if DID_SHOW_NOTIFICATION
|
||||||
|
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
cx.update(|cx| {
|
||||||
|
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
return Err(anyhow!("cannot install gopls"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let version = version.downcast::<Option<String>>().unwrap();
|
||||||
|
let this = *self;
|
||||||
|
|
||||||
|
if let Some(version) = *version {
|
||||||
|
let binary_path = container_dir.join(&format!("gopls_{version}"));
|
||||||
|
if let Ok(metadata) = fs::metadata(&binary_path).await {
|
||||||
|
if metadata.is_file() {
|
||||||
|
remove_matching(&container_dir, |entry| {
|
||||||
|
entry != binary_path && entry.file_name() != Some(OsStr::new("gobin"))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
return Ok(LanguageServerBinary {
|
||||||
|
path: binary_path.to_path_buf(),
|
||||||
|
arguments: server_binary_arguments(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(path) = this
|
||||||
|
.cached_server_binary(container_dir.clone(), delegate)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return Ok(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gobin_dir = container_dir.join("gobin");
|
||||||
|
fs::create_dir_all(&gobin_dir).await?;
|
||||||
|
let install_output = process::Command::new("go")
|
||||||
|
.env("GO111MODULE", "on")
|
||||||
|
.env("GOBIN", &gobin_dir)
|
||||||
|
.args(["install", "golang.org/x/tools/gopls@latest"])
|
||||||
|
.output()
|
||||||
|
.await?;
|
||||||
|
if !install_output.status.success() {
|
||||||
|
Err(anyhow!("failed to install gopls. Is go installed?"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let installed_binary_path = gobin_dir.join("gopls");
|
||||||
|
let version_output = process::Command::new(&installed_binary_path)
|
||||||
|
.arg("version")
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("failed to run installed gopls binary {:?}", e))?;
|
||||||
|
let version_stdout = str::from_utf8(&version_output.stdout)
|
||||||
|
.map_err(|_| anyhow!("gopls version produced invalid utf8"))?;
|
||||||
|
let version = GOPLS_VERSION_REGEX
|
||||||
|
.find(version_stdout)
|
||||||
|
.ok_or_else(|| anyhow!("failed to parse gopls version output"))?
|
||||||
|
.as_str();
|
||||||
|
let binary_path = container_dir.join(&format!("gopls_{version}"));
|
||||||
|
fs::rename(&installed_binary_path, &binary_path).await?;
|
||||||
|
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: binary_path.to_path_buf(),
|
||||||
|
arguments: server_binary_arguments(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir)
|
||||||
|
.await
|
||||||
|
.map(|mut binary| {
|
||||||
|
binary.arguments = vec!["--help".into()];
|
||||||
|
binary
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_completion(
|
||||||
|
&self,
|
||||||
|
completion: &lsp2::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
let label = &completion.label;
|
||||||
|
|
||||||
|
// Gopls returns nested fields and methods as completions.
|
||||||
|
// To syntax highlight these, combine their final component
|
||||||
|
// with their detail.
|
||||||
|
let name_offset = label.rfind('.').unwrap_or(0);
|
||||||
|
|
||||||
|
match completion.kind.zip(completion.detail.as_ref()) {
|
||||||
|
Some((lsp2::CompletionItemKind::MODULE, detail)) => {
|
||||||
|
let text = format!("{label} {detail}");
|
||||||
|
let source = Rope::from(format!("import {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 7..7 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
filter_range: 0..label.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
lsp2::CompletionItemKind::CONSTANT | lsp2::CompletionItemKind::VARIABLE,
|
||||||
|
detail,
|
||||||
|
)) => {
|
||||||
|
let text = format!("{label} {detail}");
|
||||||
|
let source =
|
||||||
|
Rope::from(format!("var {} {}", &text[name_offset..], detail).as_str());
|
||||||
|
let runs = adjust_runs(
|
||||||
|
name_offset,
|
||||||
|
language.highlight_text(&source, 4..4 + text.len()),
|
||||||
|
);
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
filter_range: 0..label.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((lsp2::CompletionItemKind::STRUCT, _)) => {
|
||||||
|
let text = format!("{label} struct {{}}");
|
||||||
|
let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
|
||||||
|
let runs = adjust_runs(
|
||||||
|
name_offset,
|
||||||
|
language.highlight_text(&source, 5..5 + text.len()),
|
||||||
|
);
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
filter_range: 0..label.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((lsp2::CompletionItemKind::INTERFACE, _)) => {
|
||||||
|
let text = format!("{label} interface {{}}");
|
||||||
|
let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
|
||||||
|
let runs = adjust_runs(
|
||||||
|
name_offset,
|
||||||
|
language.highlight_text(&source, 5..5 + text.len()),
|
||||||
|
);
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
filter_range: 0..label.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((lsp2::CompletionItemKind::FIELD, detail)) => {
|
||||||
|
let text = format!("{label} {detail}");
|
||||||
|
let source =
|
||||||
|
Rope::from(format!("type T struct {{ {} }}", &text[name_offset..]).as_str());
|
||||||
|
let runs = adjust_runs(
|
||||||
|
name_offset,
|
||||||
|
language.highlight_text(&source, 16..16 + text.len()),
|
||||||
|
);
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
filter_range: 0..label.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
lsp2::CompletionItemKind::FUNCTION | lsp2::CompletionItemKind::METHOD,
|
||||||
|
detail,
|
||||||
|
)) => {
|
||||||
|
if let Some(signature) = detail.strip_prefix("func") {
|
||||||
|
let text = format!("{label}{signature}");
|
||||||
|
let source = Rope::from(format!("func {} {{}}", &text[name_offset..]).as_str());
|
||||||
|
let runs = adjust_runs(
|
||||||
|
name_offset,
|
||||||
|
language.highlight_text(&source, 5..5 + text.len()),
|
||||||
|
);
|
||||||
|
return Some(CodeLabel {
|
||||||
|
filter_range: 0..label.len(),
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_symbol(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
kind: lsp2::SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
let (text, filter_range, display_range) = match kind {
|
||||||
|
lsp2::SymbolKind::METHOD | lsp2::SymbolKind::FUNCTION => {
|
||||||
|
let text = format!("func {} () {{}}", name);
|
||||||
|
let filter_range = 5..5 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::STRUCT => {
|
||||||
|
let text = format!("type {} struct {{}}", name);
|
||||||
|
let filter_range = 5..5 + name.len();
|
||||||
|
let display_range = 0..text.len();
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::INTERFACE => {
|
||||||
|
let text = format!("type {} interface {{}}", name);
|
||||||
|
let filter_range = 5..5 + name.len();
|
||||||
|
let display_range = 0..text.len();
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::CLASS => {
|
||||||
|
let text = format!("type {} T", name);
|
||||||
|
let filter_range = 5..5 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::CONSTANT => {
|
||||||
|
let text = format!("const {} = nil", name);
|
||||||
|
let filter_range = 6..6 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::VARIABLE => {
|
||||||
|
let text = format!("var {} = nil", name);
|
||||||
|
let filter_range = 4..4 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
lsp2::SymbolKind::MODULE => {
|
||||||
|
let text = format!("package {}", name);
|
||||||
|
let filter_range = 8..8 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(CodeLabel {
|
||||||
|
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||||
|
text: text[display_range].to_string(),
|
||||||
|
filter_range,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
(|| async move {
|
||||||
|
let mut last_binary_path = 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_file()
|
||||||
|
&& entry
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.map_or(false, |name| name.starts_with("gopls_"))
|
||||||
|
{
|
||||||
|
last_binary_path = Some(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(path) = last_binary_path {
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path,
|
||||||
|
arguments: server_binary_arguments(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("no cached binary"))
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjust_runs(
|
||||||
|
delta: usize,
|
||||||
|
mut runs: Vec<(Range<usize>, HighlightId)>,
|
||||||
|
) -> Vec<(Range<usize>, HighlightId)> {
|
||||||
|
for (range, _) in &mut runs {
|
||||||
|
range.start += delta;
|
||||||
|
range.end += delta;
|
||||||
|
}
|
||||||
|
runs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::languages::language;
|
||||||
|
use gpui2::Hsla;
|
||||||
|
use theme2::SyntaxTheme;
|
||||||
|
|
||||||
|
#[gpui2::test]
|
||||||
|
async fn test_go_label_for_completion() {
|
||||||
|
let language = language(
|
||||||
|
"go",
|
||||||
|
tree_sitter_go::language(),
|
||||||
|
Some(Arc::new(GoLspAdapter)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let theme = SyntaxTheme::new_test([
|
||||||
|
("type", Hsla::default()),
|
||||||
|
("keyword", Hsla::default()),
|
||||||
|
("function", Hsla::default()),
|
||||||
|
("number", Hsla::default()),
|
||||||
|
("property", Hsla::default()),
|
||||||
|
]);
|
||||||
|
language.set_theme(&theme);
|
||||||
|
|
||||||
|
let grammar = language.grammar().unwrap();
|
||||||
|
let highlight_function = grammar.highlight_id_for_name("function").unwrap();
|
||||||
|
let highlight_type = grammar.highlight_id_for_name("type").unwrap();
|
||||||
|
let highlight_keyword = grammar.highlight_id_for_name("keyword").unwrap();
|
||||||
|
let highlight_number = grammar.highlight_id_for_name("number").unwrap();
|
||||||
|
let highlight_field = grammar.highlight_id_for_name("property").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
language
|
||||||
|
.label_for_completion(&lsp2::CompletionItem {
|
||||||
|
kind: Some(lsp2::CompletionItemKind::FUNCTION),
|
||||||
|
label: "Hello".to_string(),
|
||||||
|
detail: Some("func(a B) c.D".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
Some(CodeLabel {
|
||||||
|
text: "Hello(a B) c.D".to_string(),
|
||||||
|
filter_range: 0..5,
|
||||||
|
runs: vec![
|
||||||
|
(0..5, highlight_function),
|
||||||
|
(8..9, highlight_type),
|
||||||
|
(13..14, highlight_type),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Nested methods
|
||||||
|
assert_eq!(
|
||||||
|
language
|
||||||
|
.label_for_completion(&lsp2::CompletionItem {
|
||||||
|
kind: Some(lsp2::CompletionItemKind::METHOD),
|
||||||
|
label: "one.two.Three".to_string(),
|
||||||
|
detail: Some("func() [3]interface{}".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
Some(CodeLabel {
|
||||||
|
text: "one.two.Three() [3]interface{}".to_string(),
|
||||||
|
filter_range: 0..13,
|
||||||
|
runs: vec![
|
||||||
|
(8..13, highlight_function),
|
||||||
|
(17..18, highlight_number),
|
||||||
|
(19..28, highlight_keyword),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Nested fields
|
||||||
|
assert_eq!(
|
||||||
|
language
|
||||||
|
.label_for_completion(&lsp2::CompletionItem {
|
||||||
|
kind: Some(lsp2::CompletionItemKind::FIELD),
|
||||||
|
label: "two.Three".to_string(),
|
||||||
|
detail: Some("a.Bcd".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
Some(CodeLabel {
|
||||||
|
text: "two.Three a.Bcd".to_string(),
|
||||||
|
filter_range: 0..9,
|
||||||
|
runs: vec![(4..9, highlight_field), (12..15, highlight_type)],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
3
crates/zed2/src/languages/go/brackets.scm
Normal file
3
crates/zed2/src/languages/go/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("\"" @open "\"" @close)
|
12
crates/zed2/src/languages/go/config.toml
Normal file
12
crates/zed2/src/languages/go/config.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name = "Go"
|
||||||
|
path_suffixes = ["go"]
|
||||||
|
line_comment = "// "
|
||||||
|
autoclose_before = ";:.,=}])>"
|
||||||
|
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, not_in = ["comment", "string"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
{ start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
]
|
24
crates/zed2/src/languages/go/embedding.scm
Normal file
24
crates/zed2/src/languages/go/embedding.scm
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(type_declaration
|
||||||
|
(type_spec
|
||||||
|
name: (_) @name)
|
||||||
|
) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(function_declaration
|
||||||
|
name: (_) @name
|
||||||
|
) @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(method_declaration
|
||||||
|
name: (_) @name
|
||||||
|
) @item
|
||||||
|
)
|
107
crates/zed2/src/languages/go/highlights.scm
Normal file
107
crates/zed2/src/languages/go/highlights.scm
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
(identifier) @variable
|
||||||
|
(type_identifier) @type
|
||||||
|
(field_identifier) @property
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @function)
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (selector_expression
|
||||||
|
field: (field_identifier) @function.method))
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
name: (identifier) @function)
|
||||||
|
|
||||||
|
(method_declaration
|
||||||
|
name: (field_identifier) @function.method)
|
||||||
|
|
||||||
|
[
|
||||||
|
"--"
|
||||||
|
"-"
|
||||||
|
"-="
|
||||||
|
":="
|
||||||
|
"!"
|
||||||
|
"!="
|
||||||
|
"..."
|
||||||
|
"*"
|
||||||
|
"*"
|
||||||
|
"*="
|
||||||
|
"/"
|
||||||
|
"/="
|
||||||
|
"&"
|
||||||
|
"&&"
|
||||||
|
"&="
|
||||||
|
"%"
|
||||||
|
"%="
|
||||||
|
"^"
|
||||||
|
"^="
|
||||||
|
"+"
|
||||||
|
"++"
|
||||||
|
"+="
|
||||||
|
"<-"
|
||||||
|
"<"
|
||||||
|
"<<"
|
||||||
|
"<<="
|
||||||
|
"<="
|
||||||
|
"="
|
||||||
|
"=="
|
||||||
|
">"
|
||||||
|
">="
|
||||||
|
">>"
|
||||||
|
">>="
|
||||||
|
"|"
|
||||||
|
"|="
|
||||||
|
"||"
|
||||||
|
"~"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
"break"
|
||||||
|
"case"
|
||||||
|
"chan"
|
||||||
|
"const"
|
||||||
|
"continue"
|
||||||
|
"default"
|
||||||
|
"defer"
|
||||||
|
"else"
|
||||||
|
"fallthrough"
|
||||||
|
"for"
|
||||||
|
"func"
|
||||||
|
"go"
|
||||||
|
"goto"
|
||||||
|
"if"
|
||||||
|
"import"
|
||||||
|
"interface"
|
||||||
|
"map"
|
||||||
|
"package"
|
||||||
|
"range"
|
||||||
|
"return"
|
||||||
|
"select"
|
||||||
|
"struct"
|
||||||
|
"switch"
|
||||||
|
"type"
|
||||||
|
"var"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
(interpreted_string_literal)
|
||||||
|
(raw_string_literal)
|
||||||
|
(rune_literal)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
(escape_sequence) @escape
|
||||||
|
|
||||||
|
[
|
||||||
|
(int_literal)
|
||||||
|
(float_literal)
|
||||||
|
(imaginary_literal)
|
||||||
|
] @number
|
||||||
|
|
||||||
|
[
|
||||||
|
(true)
|
||||||
|
(false)
|
||||||
|
(nil)
|
||||||
|
(iota)
|
||||||
|
] @constant.builtin
|
||||||
|
|
||||||
|
(comment) @comment
|
9
crates/zed2/src/languages/go/indents.scm
Normal file
9
crates/zed2/src/languages/go/indents.scm
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[
|
||||||
|
(assignment_statement)
|
||||||
|
(call_expression)
|
||||||
|
(selector_expression)
|
||||||
|
] @indent
|
||||||
|
|
||||||
|
(_ "[" "]" @end) @indent
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
43
crates/zed2/src/languages/go/outline.scm
Normal file
43
crates/zed2/src/languages/go/outline.scm
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
(type_declaration
|
||||||
|
"type" @context
|
||||||
|
(type_spec
|
||||||
|
name: (_) @name)) @item
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
"func" @context
|
||||||
|
name: (identifier) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(method_declaration
|
||||||
|
"func" @context
|
||||||
|
receiver: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
(parameter_declaration
|
||||||
|
type: (_) @context)
|
||||||
|
")" @context)
|
||||||
|
name: (field_identifier) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(const_declaration
|
||||||
|
"const" @context
|
||||||
|
(const_spec
|
||||||
|
name: (identifier) @name) @item)
|
||||||
|
|
||||||
|
(source_file
|
||||||
|
(var_declaration
|
||||||
|
"var" @context
|
||||||
|
(var_spec
|
||||||
|
name: (identifier) @name) @item))
|
||||||
|
|
||||||
|
(method_spec
|
||||||
|
name: (_) @name
|
||||||
|
parameters: (parameter_list
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(field_declaration
|
||||||
|
name: (_) @name) @item
|
6
crates/zed2/src/languages/go/overrides.scm
Normal file
6
crates/zed2/src/languages/go/overrides.scm
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
(comment) @comment
|
||||||
|
[
|
||||||
|
(interpreted_string_literal)
|
||||||
|
(raw_string_literal)
|
||||||
|
(rune_literal)
|
||||||
|
] @string
|
12
crates/zed2/src/languages/heex/config.toml
Normal file
12
crates/zed2/src/languages/heex/config.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name = "HEEX"
|
||||||
|
path_suffixes = ["heex"]
|
||||||
|
autoclose_before = ">})"
|
||||||
|
brackets = [
|
||||||
|
{ start = "<", end = ">", close = true, newline = true },
|
||||||
|
]
|
||||||
|
block_comment = ["<%!-- ", " --%>"]
|
||||||
|
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||||
|
|
||||||
|
[overrides.string]
|
||||||
|
word_characters = ["-"]
|
||||||
|
opt_into_language_servers = ["tailwindcss-language-server"]
|
57
crates/zed2/src/languages/heex/highlights.scm
Normal file
57
crates/zed2/src/languages/heex/highlights.scm
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
; HEEx delimiters
|
||||||
|
[
|
||||||
|
"/>"
|
||||||
|
"<!"
|
||||||
|
"<"
|
||||||
|
"</"
|
||||||
|
"</:"
|
||||||
|
"<:"
|
||||||
|
">"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
"<%!--"
|
||||||
|
"<%"
|
||||||
|
"<%#"
|
||||||
|
"<%%="
|
||||||
|
"<%="
|
||||||
|
"%>"
|
||||||
|
"--%>"
|
||||||
|
"-->"
|
||||||
|
"<!--"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
; HEEx operators are highlighted as such
|
||||||
|
"=" @operator
|
||||||
|
|
||||||
|
; HEEx inherits the DOCTYPE tag from HTML
|
||||||
|
(doctype) @constant
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
; HEEx tags and slots are highlighted as HTML
|
||||||
|
[
|
||||||
|
(tag_name)
|
||||||
|
(slot_name)
|
||||||
|
] @tag
|
||||||
|
|
||||||
|
; HEEx attributes are highlighted as HTML attributes
|
||||||
|
(attribute_name) @attribute
|
||||||
|
|
||||||
|
; HEEx special attributes are highlighted as keywords
|
||||||
|
(special_attribute_name) @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
(attribute_value)
|
||||||
|
(quoted_attribute_value)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
; HEEx components are highlighted as Elixir modules and functions
|
||||||
|
(component_name
|
||||||
|
[
|
||||||
|
(module) @module
|
||||||
|
(function) @function
|
||||||
|
"." @punctuation.delimiter
|
||||||
|
])
|
13
crates/zed2/src/languages/heex/injections.scm
Normal file
13
crates/zed2/src/languages/heex/injections.scm
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
(
|
||||||
|
(directive
|
||||||
|
[
|
||||||
|
(partial_expression_value)
|
||||||
|
(expression_value)
|
||||||
|
(ending_expression_value)
|
||||||
|
] @content)
|
||||||
|
(#set! language "elixir")
|
||||||
|
(#set! combined)
|
||||||
|
)
|
||||||
|
|
||||||
|
((expression (expression_value) @content)
|
||||||
|
(#set! language "elixir"))
|
4
crates/zed2/src/languages/heex/overrides.scm
Normal file
4
crates/zed2/src/languages/heex/overrides.scm
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[
|
||||||
|
(attribute_value)
|
||||||
|
(quoted_attribute_value)
|
||||||
|
] @string
|
130
crates/zed2/src/languages/html.rs
Normal file
130
crates/zed2/src/languages/html.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
|
use lsp2::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::ResultExt;
|
||||||
|
|
||||||
|
const SERVER_PATH: &'static str =
|
||||||
|
"node_modules/vscode-langservers-extracted/bin/vscode-html-language-server";
|
||||||
|
|
||||||
|
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||||
|
vec![server_path.into(), "--stdio".into()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HtmlLspAdapter {
|
||||||
|
node: Arc<dyn NodeRuntime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HtmlLspAdapter {
|
||||||
|
pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
|
||||||
|
HtmlLspAdapter { node }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for HtmlLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("vscode-html-language-server".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"html"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||||
|
Ok(Box::new(
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version("vscode-langservers-extracted")
|
||||||
|
.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,
|
||||||
|
&[("vscode-langservers-extracted", 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
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||||
|
Some(json!({
|
||||||
|
"provideFormatter": true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(
|
||||||
|
container_dir: PathBuf,
|
||||||
|
node: &dyn NodeRuntime,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
(|| async move {
|
||||||
|
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()
|
||||||
|
}
|
2
crates/zed2/src/languages/html/brackets.scm
Normal file
2
crates/zed2/src/languages/html/brackets.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
("<" @open ">" @close)
|
||||||
|
("\"" @open "\"" @close)
|
14
crates/zed2/src/languages/html/config.toml
Normal file
14
crates/zed2/src/languages/html/config.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name = "HTML"
|
||||||
|
path_suffixes = ["html"]
|
||||||
|
autoclose_before = ">})"
|
||||||
|
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, not_in = ["comment", "string"] },
|
||||||
|
{ start = "<", end = ">", close = true, newline = true, not_in = ["comment", "string"] },
|
||||||
|
{ start = "!--", end = " --", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
]
|
||||||
|
word_characters = ["-"]
|
||||||
|
prettier_parser_name = "html"
|
15
crates/zed2/src/languages/html/highlights.scm
Normal file
15
crates/zed2/src/languages/html/highlights.scm
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
(tag_name) @keyword
|
||||||
|
(erroneous_end_tag_name) @keyword
|
||||||
|
(doctype) @constant
|
||||||
|
(attribute_name) @property
|
||||||
|
(attribute_value) @string
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
"=" @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
"<"
|
||||||
|
">"
|
||||||
|
"</"
|
||||||
|
"/>"
|
||||||
|
] @punctuation.bracket
|
6
crates/zed2/src/languages/html/indents.scm
Normal file
6
crates/zed2/src/languages/html/indents.scm
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
(start_tag ">" @end) @indent
|
||||||
|
(self_closing_tag "/>" @end) @indent
|
||||||
|
|
||||||
|
(element
|
||||||
|
(start_tag) @start
|
||||||
|
(end_tag)? @end) @indent
|
7
crates/zed2/src/languages/html/injections.scm
Normal file
7
crates/zed2/src/languages/html/injections.scm
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
(script_element
|
||||||
|
(raw_text) @content
|
||||||
|
(#set! "language" "javascript"))
|
||||||
|
|
||||||
|
(style_element
|
||||||
|
(raw_text) @content
|
||||||
|
(#set! "language" "css"))
|
0
crates/zed2/src/languages/html/outline.scm
Normal file
0
crates/zed2/src/languages/html/outline.scm
Normal file
2
crates/zed2/src/languages/html/overrides.scm
Normal file
2
crates/zed2/src/languages/html/overrides.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(comment) @comment
|
||||||
|
(quoted_attribute_value) @string
|
5
crates/zed2/src/languages/javascript/brackets.scm
Normal file
5
crates/zed2/src/languages/javascript/brackets.scm
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
("(" @open ")" @close)
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("<" @open ">" @close)
|
||||||
|
("\"" @open "\"" @close)
|
26
crates/zed2/src/languages/javascript/config.toml
Normal file
26
crates/zed2/src/languages/javascript/config.toml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name = "JavaScript"
|
||||||
|
path_suffixes = ["js", "jsx", "mjs", "cjs"]
|
||||||
|
first_line_pattern = '^#!.*\bnode\b'
|
||||||
|
line_comment = "// "
|
||||||
|
autoclose_before = ";:.,=}])>"
|
||||||
|
brackets = [
|
||||||
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
|
{ start = "(", end = ")", close = true, newline = true },
|
||||||
|
{ start = "<", end = ">", close = false, newline = true, not_in = ["comment", "string"] },
|
||||||
|
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
{ start = "`", end = "`", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
{ start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
|
||||||
|
]
|
||||||
|
word_characters = ["$", "#"]
|
||||||
|
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||||
|
prettier_parser_name = "babel"
|
||||||
|
|
||||||
|
[overrides.element]
|
||||||
|
line_comment = { remove = true }
|
||||||
|
block_comment = ["{/* ", " */}"]
|
||||||
|
|
||||||
|
[overrides.string]
|
||||||
|
word_characters = ["-"]
|
||||||
|
opt_into_language_servers = ["tailwindcss-language-server"]
|
0
crates/zed2/src/languages/javascript/contexts.scm
Normal file
0
crates/zed2/src/languages/javascript/contexts.scm
Normal file
71
crates/zed2/src/languages/javascript/embedding.scm
Normal file
71
crates/zed2/src/languages/javascript/embedding.scm
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(export_statement
|
||||||
|
(function_declaration
|
||||||
|
"async"? @name
|
||||||
|
"function" @name
|
||||||
|
name: (_) @name))
|
||||||
|
(function_declaration
|
||||||
|
"async"? @name
|
||||||
|
"function" @name
|
||||||
|
name: (_) @name)
|
||||||
|
] @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(export_statement
|
||||||
|
(class_declaration
|
||||||
|
"class" @name
|
||||||
|
name: (_) @name))
|
||||||
|
(class_declaration
|
||||||
|
"class" @name
|
||||||
|
name: (_) @name)
|
||||||
|
] @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(export_statement
|
||||||
|
(interface_declaration
|
||||||
|
"interface" @name
|
||||||
|
name: (_) @name))
|
||||||
|
(interface_declaration
|
||||||
|
"interface" @name
|
||||||
|
name: (_) @name)
|
||||||
|
] @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(export_statement
|
||||||
|
(enum_declaration
|
||||||
|
"enum" @name
|
||||||
|
name: (_) @name))
|
||||||
|
(enum_declaration
|
||||||
|
"enum" @name
|
||||||
|
name: (_) @name)
|
||||||
|
] @item
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(method_definition
|
||||||
|
[
|
||||||
|
"get"
|
||||||
|
"set"
|
||||||
|
"async"
|
||||||
|
"*"
|
||||||
|
"static"
|
||||||
|
]* @name
|
||||||
|
name: (_) @name) @item
|
||||||
|
)
|
217
crates/zed2/src/languages/javascript/highlights.scm
Normal file
217
crates/zed2/src/languages/javascript/highlights.scm
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
; Variables
|
||||||
|
|
||||||
|
(identifier) @variable
|
||||||
|
|
||||||
|
; Properties
|
||||||
|
|
||||||
|
(property_identifier) @property
|
||||||
|
|
||||||
|
; Function and method calls
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @function)
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (member_expression
|
||||||
|
property: (property_identifier) @function.method))
|
||||||
|
|
||||||
|
; Function and method definitions
|
||||||
|
|
||||||
|
(function
|
||||||
|
name: (identifier) @function)
|
||||||
|
(function_declaration
|
||||||
|
name: (identifier) @function)
|
||||||
|
(method_definition
|
||||||
|
name: (property_identifier) @function.method)
|
||||||
|
|
||||||
|
(pair
|
||||||
|
key: (property_identifier) @function.method
|
||||||
|
value: [(function) (arrow_function)])
|
||||||
|
|
||||||
|
(assignment_expression
|
||||||
|
left: (member_expression
|
||||||
|
property: (property_identifier) @function.method)
|
||||||
|
right: [(function) (arrow_function)])
|
||||||
|
|
||||||
|
(variable_declarator
|
||||||
|
name: (identifier) @function
|
||||||
|
value: [(function) (arrow_function)])
|
||||||
|
|
||||||
|
(assignment_expression
|
||||||
|
left: (identifier) @function
|
||||||
|
right: [(function) (arrow_function)])
|
||||||
|
|
||||||
|
; Special identifiers
|
||||||
|
|
||||||
|
((identifier) @type
|
||||||
|
(#match? @type "^[A-Z]"))
|
||||||
|
(type_identifier) @type
|
||||||
|
(predefined_type) @type.builtin
|
||||||
|
|
||||||
|
([
|
||||||
|
(identifier)
|
||||||
|
(shorthand_property_identifier)
|
||||||
|
(shorthand_property_identifier_pattern)
|
||||||
|
] @constant
|
||||||
|
(#match? @constant "^_*[A-Z_][A-Z\\d_]*$"))
|
||||||
|
|
||||||
|
; Literals
|
||||||
|
|
||||||
|
(this) @variable.special
|
||||||
|
(super) @variable.special
|
||||||
|
|
||||||
|
[
|
||||||
|
(null)
|
||||||
|
(undefined)
|
||||||
|
] @constant.builtin
|
||||||
|
|
||||||
|
[
|
||||||
|
(true)
|
||||||
|
(false)
|
||||||
|
] @boolean
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
(string)
|
||||||
|
(template_string)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
(regex) @string.regex
|
||||||
|
(number) @number
|
||||||
|
|
||||||
|
; Tokens
|
||||||
|
|
||||||
|
[
|
||||||
|
";"
|
||||||
|
"?."
|
||||||
|
"."
|
||||||
|
","
|
||||||
|
":"
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
[
|
||||||
|
"-"
|
||||||
|
"--"
|
||||||
|
"-="
|
||||||
|
"+"
|
||||||
|
"++"
|
||||||
|
"+="
|
||||||
|
"*"
|
||||||
|
"*="
|
||||||
|
"**"
|
||||||
|
"**="
|
||||||
|
"/"
|
||||||
|
"/="
|
||||||
|
"%"
|
||||||
|
"%="
|
||||||
|
"<"
|
||||||
|
"<="
|
||||||
|
"<<"
|
||||||
|
"<<="
|
||||||
|
"="
|
||||||
|
"=="
|
||||||
|
"==="
|
||||||
|
"!"
|
||||||
|
"!="
|
||||||
|
"!=="
|
||||||
|
"=>"
|
||||||
|
">"
|
||||||
|
">="
|
||||||
|
">>"
|
||||||
|
">>="
|
||||||
|
">>>"
|
||||||
|
">>>="
|
||||||
|
"~"
|
||||||
|
"^"
|
||||||
|
"&"
|
||||||
|
"|"
|
||||||
|
"^="
|
||||||
|
"&="
|
||||||
|
"|="
|
||||||
|
"&&"
|
||||||
|
"||"
|
||||||
|
"??"
|
||||||
|
"&&="
|
||||||
|
"||="
|
||||||
|
"??="
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
"as"
|
||||||
|
"async"
|
||||||
|
"await"
|
||||||
|
"break"
|
||||||
|
"case"
|
||||||
|
"catch"
|
||||||
|
"class"
|
||||||
|
"const"
|
||||||
|
"continue"
|
||||||
|
"debugger"
|
||||||
|
"default"
|
||||||
|
"delete"
|
||||||
|
"do"
|
||||||
|
"else"
|
||||||
|
"export"
|
||||||
|
"extends"
|
||||||
|
"finally"
|
||||||
|
"for"
|
||||||
|
"from"
|
||||||
|
"function"
|
||||||
|
"get"
|
||||||
|
"if"
|
||||||
|
"import"
|
||||||
|
"in"
|
||||||
|
"instanceof"
|
||||||
|
"let"
|
||||||
|
"new"
|
||||||
|
"of"
|
||||||
|
"return"
|
||||||
|
"set"
|
||||||
|
"static"
|
||||||
|
"switch"
|
||||||
|
"target"
|
||||||
|
"throw"
|
||||||
|
"try"
|
||||||
|
"typeof"
|
||||||
|
"var"
|
||||||
|
"void"
|
||||||
|
"while"
|
||||||
|
"with"
|
||||||
|
"yield"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(template_substitution
|
||||||
|
"${" @punctuation.special
|
||||||
|
"}" @punctuation.special) @embedded
|
||||||
|
|
||||||
|
(type_arguments
|
||||||
|
"<" @punctuation.bracket
|
||||||
|
">" @punctuation.bracket)
|
||||||
|
|
||||||
|
; Keywords
|
||||||
|
|
||||||
|
[ "abstract"
|
||||||
|
"declare"
|
||||||
|
"enum"
|
||||||
|
"export"
|
||||||
|
"implements"
|
||||||
|
"interface"
|
||||||
|
"keyof"
|
||||||
|
"namespace"
|
||||||
|
"private"
|
||||||
|
"protected"
|
||||||
|
"public"
|
||||||
|
"type"
|
||||||
|
"readonly"
|
||||||
|
"override"
|
||||||
|
] @keyword
|
15
crates/zed2/src/languages/javascript/indents.scm
Normal file
15
crates/zed2/src/languages/javascript/indents.scm
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[
|
||||||
|
(call_expression)
|
||||||
|
(assignment_expression)
|
||||||
|
(member_expression)
|
||||||
|
(lexical_declaration)
|
||||||
|
(variable_declaration)
|
||||||
|
(assignment_expression)
|
||||||
|
(if_statement)
|
||||||
|
(for_statement)
|
||||||
|
] @indent
|
||||||
|
|
||||||
|
(_ "[" "]" @end) @indent
|
||||||
|
(_ "<" ">" @end) @indent
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
62
crates/zed2/src/languages/javascript/outline.scm
Normal file
62
crates/zed2/src/languages/javascript/outline.scm
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
(internal_module
|
||||||
|
"namespace" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(enum_declaration
|
||||||
|
"enum" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
"async"? @context
|
||||||
|
"function" @context
|
||||||
|
name: (_) @name
|
||||||
|
parameters: (formal_parameters
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(interface_declaration
|
||||||
|
"interface" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(program
|
||||||
|
(export_statement
|
||||||
|
(lexical_declaration
|
||||||
|
["let" "const"] @context
|
||||||
|
(variable_declarator
|
||||||
|
name: (_) @name) @item)))
|
||||||
|
|
||||||
|
(program
|
||||||
|
(lexical_declaration
|
||||||
|
["let" "const"] @context
|
||||||
|
(variable_declarator
|
||||||
|
name: (_) @name) @item))
|
||||||
|
|
||||||
|
(class_declaration
|
||||||
|
"class" @context
|
||||||
|
name: (_) @name) @item
|
||||||
|
|
||||||
|
(method_definition
|
||||||
|
[
|
||||||
|
"get"
|
||||||
|
"set"
|
||||||
|
"async"
|
||||||
|
"*"
|
||||||
|
"readonly"
|
||||||
|
"static"
|
||||||
|
(override_modifier)
|
||||||
|
(accessibility_modifier)
|
||||||
|
]* @context
|
||||||
|
name: (_) @name
|
||||||
|
parameters: (formal_parameters
|
||||||
|
"(" @context
|
||||||
|
")" @context)) @item
|
||||||
|
|
||||||
|
(public_field_definition
|
||||||
|
[
|
||||||
|
"declare"
|
||||||
|
"readonly"
|
||||||
|
"abstract"
|
||||||
|
"static"
|
||||||
|
(accessibility_modifier)
|
||||||
|
]* @context
|
||||||
|
name: (_) @name) @item
|
13
crates/zed2/src/languages/javascript/overrides.scm
Normal file
13
crates/zed2/src/languages/javascript/overrides.scm
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
(string)
|
||||||
|
(template_string)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
[
|
||||||
|
(jsx_element)
|
||||||
|
(jsx_fragment)
|
||||||
|
(jsx_self_closing_element)
|
||||||
|
(jsx_expression)
|
||||||
|
] @element
|
184
crates/zed2/src/languages/json.rs
Normal file
184
crates/zed2/src/languages/json.rs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use collections::HashMap;
|
||||||
|
use feature_flags2::FeatureFlagAppExt;
|
||||||
|
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||||
|
use gpui2::AppContext;
|
||||||
|
use language2::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
|
use lsp2::LanguageServerBinary;
|
||||||
|
use node_runtime::NodeRuntime;
|
||||||
|
use serde_json::json;
|
||||||
|
use settings2::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
|
||||||
|
use smol::fs;
|
||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
ffi::OsString,
|
||||||
|
future,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use util::{paths, ResultExt};
|
||||||
|
|
||||||
|
const SERVER_PATH: &'static str =
|
||||||
|
"node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
|
||||||
|
|
||||||
|
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||||
|
vec![server_path.into(), "--stdio".into()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JsonLspAdapter {
|
||||||
|
node: Arc<dyn NodeRuntime>,
|
||||||
|
languages: Arc<LanguageRegistry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsonLspAdapter {
|
||||||
|
pub fn new(node: Arc<dyn NodeRuntime>, languages: Arc<LanguageRegistry>) -> Self {
|
||||||
|
JsonLspAdapter { node, languages }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for JsonLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("json-language-server".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"json"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
Ok(Box::new(
|
||||||
|
self.node
|
||||||
|
.npm_package_latest_version("vscode-json-languageserver")
|
||||||
|
.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,
|
||||||
|
&[("vscode-json-languageserver", 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
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||||
|
Some(json!({
|
||||||
|
"provideFormatter": true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspace_configuration(
|
||||||
|
&self,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> BoxFuture<'static, serde_json::Value> {
|
||||||
|
let action_names = cx.all_action_names().collect::<Vec<_>>();
|
||||||
|
let staff_mode = cx.is_staff();
|
||||||
|
let language_names = &self.languages.language_names();
|
||||||
|
let settings_schema = cx.global::<SettingsStore>().json_schema(
|
||||||
|
&SettingsJsonSchemaParams {
|
||||||
|
language_names,
|
||||||
|
staff_mode,
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
future::ready(serde_json::json!({
|
||||||
|
"json": {
|
||||||
|
"format": {
|
||||||
|
"enable": true,
|
||||||
|
},
|
||||||
|
"schemas": [
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
schema_file_match(&paths::SETTINGS),
|
||||||
|
&*paths::LOCAL_SETTINGS_RELATIVE_PATH,
|
||||||
|
],
|
||||||
|
"schema": settings_schema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": [schema_file_match(&paths::KEYMAP)],
|
||||||
|
"schema": KeymapFile::generate_json_schema(&action_names),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn language_ids(&self) -> HashMap<String, String> {
|
||||||
|
[("JSON".into(), "jsonc".into())].into_iter().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(
|
||||||
|
container_dir: PathBuf,
|
||||||
|
node: &dyn NodeRuntime,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
(|| async move {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schema_file_match(path: &Path) -> &Path {
|
||||||
|
path.strip_prefix(path.parent().unwrap().parent().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
}
|
3
crates/zed2/src/languages/json/brackets.scm
Normal file
3
crates/zed2/src/languages/json/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("\"" @open "\"" @close)
|
10
crates/zed2/src/languages/json/config.toml
Normal file
10
crates/zed2/src/languages/json/config.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
name = "JSON"
|
||||||
|
path_suffixes = ["json"]
|
||||||
|
line_comment = "// "
|
||||||
|
autoclose_before = ",]}"
|
||||||
|
brackets = [
|
||||||
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
|
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||||
|
]
|
||||||
|
prettier_parser_name = "json"
|
14
crates/zed2/src/languages/json/embedding.scm
Normal file
14
crates/zed2/src/languages/json/embedding.scm
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
; Only produce one embedding for the entire file.
|
||||||
|
(document) @item
|
||||||
|
|
||||||
|
; Collapse arrays, except for the first object.
|
||||||
|
(array
|
||||||
|
"[" @keep
|
||||||
|
.
|
||||||
|
(object)? @keep
|
||||||
|
"]" @keep) @collapse
|
||||||
|
|
||||||
|
; Collapse string values (but not keys).
|
||||||
|
(pair value: (string
|
||||||
|
"\"" @keep
|
||||||
|
"\"" @keep) @collapse)
|
21
crates/zed2/src/languages/json/highlights.scm
Normal file
21
crates/zed2/src/languages/json/highlights.scm
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
(string) @string
|
||||||
|
|
||||||
|
(pair
|
||||||
|
key: (string) @property)
|
||||||
|
|
||||||
|
(number) @number
|
||||||
|
|
||||||
|
[
|
||||||
|
(true)
|
||||||
|
(false)
|
||||||
|
(null)
|
||||||
|
] @constant
|
||||||
|
|
||||||
|
[
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
] @punctuation.bracket
|
2
crates/zed2/src/languages/json/indents.scm
Normal file
2
crates/zed2/src/languages/json/indents.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(array "]" @end) @indent
|
||||||
|
(object "}" @end) @indent
|
2
crates/zed2/src/languages/json/outline.scm
Normal file
2
crates/zed2/src/languages/json/outline.scm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(pair
|
||||||
|
key: (string (string_content) @name)) @item
|
1
crates/zed2/src/languages/json/overrides.scm
Normal file
1
crates/zed2/src/languages/json/overrides.scm
Normal file
|
@ -0,0 +1 @@
|
||||||
|
(string) @string
|
168
crates/zed2/src/languages/language_plugin.rs
Normal file
168
crates/zed2/src/languages/language_plugin.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use collections::HashMap;
|
||||||
|
use futures::lock::Mutex;
|
||||||
|
use gpui2::executor::Background;
|
||||||
|
use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
|
use lsp2::LanguageServerBinary;
|
||||||
|
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
|
||||||
|
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||||
|
use util::ResultExt;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub async fn new_json(executor: Arc<Background>) -> Result<PluginLspAdapter> {
|
||||||
|
let plugin = PluginBuilder::new_default()?
|
||||||
|
.host_function_async("command", |command: String| async move {
|
||||||
|
let mut args = command.split(' ');
|
||||||
|
let command = args.next().unwrap();
|
||||||
|
smol::process::Command::new(command)
|
||||||
|
.args(args)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
.map(|output| output.stdout)
|
||||||
|
})?
|
||||||
|
.init(PluginBinary::Precompiled(include_bytes!(
|
||||||
|
"../../../../plugins/bin/json_language.wasm.pre",
|
||||||
|
)))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
PluginLspAdapter::new(plugin, executor).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PluginLspAdapter {
|
||||||
|
name: WasiFn<(), String>,
|
||||||
|
fetch_latest_server_version: WasiFn<(), Option<String>>,
|
||||||
|
fetch_server_binary: WasiFn<(PathBuf, String), Result<LanguageServerBinary, String>>,
|
||||||
|
cached_server_binary: WasiFn<PathBuf, Option<LanguageServerBinary>>,
|
||||||
|
initialization_options: WasiFn<(), String>,
|
||||||
|
language_ids: WasiFn<(), Vec<(String, String)>>,
|
||||||
|
executor: Arc<Background>,
|
||||||
|
runtime: Arc<Mutex<Plugin>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginLspAdapter {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub async fn new(mut plugin: Plugin, executor: Arc<Background>) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
name: plugin.function("name")?,
|
||||||
|
fetch_latest_server_version: plugin.function("fetch_latest_server_version")?,
|
||||||
|
fetch_server_binary: plugin.function("fetch_server_binary")?,
|
||||||
|
cached_server_binary: plugin.function("cached_server_binary")?,
|
||||||
|
initialization_options: plugin.function("initialization_options")?,
|
||||||
|
language_ids: plugin.function("language_ids")?,
|
||||||
|
executor,
|
||||||
|
runtime: Arc::new(Mutex::new(plugin)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for PluginLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
let name: String = self
|
||||||
|
.runtime
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.call(&self.name, ())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
LanguageServerName(name.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"PluginLspAdapter"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
let runtime = self.runtime.clone();
|
||||||
|
let function = self.fetch_latest_server_version;
|
||||||
|
self.executor
|
||||||
|
.spawn(async move {
|
||||||
|
let mut runtime = runtime.lock().await;
|
||||||
|
let versions: Result<Option<String>> =
|
||||||
|
runtime.call::<_, Option<String>>(&function, ()).await;
|
||||||
|
versions
|
||||||
|
.map_err(|e| anyhow!("{}", e))?
|
||||||
|
.ok_or_else(|| anyhow!("Could not fetch latest server version"))
|
||||||
|
.map(|v| Box::new(v) as Box<_>)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
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 runtime = self.runtime.clone();
|
||||||
|
let function = self.fetch_server_binary;
|
||||||
|
self.executor
|
||||||
|
.spawn(async move {
|
||||||
|
let mut runtime = runtime.lock().await;
|
||||||
|
let handle = runtime.attach_path(&container_dir)?;
|
||||||
|
let result: Result<LanguageServerBinary, String> =
|
||||||
|
runtime.call(&function, (container_dir, version)).await?;
|
||||||
|
runtime.remove_resource(handle)?;
|
||||||
|
result.map_err(|e| anyhow!("{}", e))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
let runtime = self.runtime.clone();
|
||||||
|
let function = self.cached_server_binary;
|
||||||
|
|
||||||
|
self.executor
|
||||||
|
.spawn(async move {
|
||||||
|
let mut runtime = runtime.lock().await;
|
||||||
|
let handle = runtime.attach_path(&container_dir).ok()?;
|
||||||
|
let result: Option<LanguageServerBinary> =
|
||||||
|
runtime.call(&function, container_dir).await.ok()?;
|
||||||
|
runtime.remove_resource(handle).ok()?;
|
||||||
|
result
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_be_reinstalled(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||||
|
let string: String = self
|
||||||
|
.runtime
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.call(&self.initialization_options, ())
|
||||||
|
.await
|
||||||
|
.log_err()?;
|
||||||
|
|
||||||
|
serde_json::from_str(&string).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn language_ids(&self) -> HashMap<String, String> {
|
||||||
|
self.runtime
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.call(&self.language_ids, ())
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
135
crates/zed2/src/languages/lua.rs
Normal file
135
crates/zed2/src/languages/lua.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use async_compression::futures::bufread::GzipDecoder;
|
||||||
|
use async_tar::Archive;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::{io::BufReader, StreamExt};
|
||||||
|
use language2::{LanguageServerName, LspAdapterDelegate};
|
||||||
|
use lsp2::LanguageServerBinary;
|
||||||
|
use smol::fs;
|
||||||
|
use std::{any::Any, env::consts, path::PathBuf};
|
||||||
|
use util::{
|
||||||
|
async_maybe,
|
||||||
|
github::{latest_github_release, GitHubLspBinaryVersion},
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct LuaLspAdapter;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl super::LspAdapter for LuaLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("lua-language-server".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_name(&self) -> &'static str {
|
||||||
|
"lua"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_latest_server_version(
|
||||||
|
&self,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
|
let release =
|
||||||
|
latest_github_release("LuaLS/lua-language-server", false, delegate.http_client())
|
||||||
|
.await?;
|
||||||
|
let version = release.name.clone();
|
||||||
|
let platform = match consts::ARCH {
|
||||||
|
"x86_64" => "x64",
|
||||||
|
"aarch64" => "arm64",
|
||||||
|
other => bail!("Running on unsupported platform: {other}"),
|
||||||
|
};
|
||||||
|
let asset_name = format!("lua-language-server-{version}-darwin-{platform}.tar.gz");
|
||||||
|
let asset = release
|
||||||
|
.assets
|
||||||
|
.iter()
|
||||||
|
.find(|asset| asset.name == asset_name)
|
||||||
|
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||||
|
let version = GitHubLspBinaryVersion {
|
||||||
|
name: release.name.clone(),
|
||||||
|
url: asset.browser_download_url.clone(),
|
||||||
|
};
|
||||||
|
Ok(Box::new(version) as Box<_>)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_server_binary(
|
||||||
|
&self,
|
||||||
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||||
|
|
||||||
|
let binary_path = container_dir.join("bin/lua-language-server");
|
||||||
|
|
||||||
|
if fs::metadata(&binary_path).await.is_err() {
|
||||||
|
let mut response = delegate
|
||||||
|
.http_client()
|
||||||
|
.get(&version.url, Default::default(), true)
|
||||||
|
.await
|
||||||
|
.map_err(|err| anyhow!("error downloading release: {}", err))?;
|
||||||
|
let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
|
||||||
|
let archive = Archive::new(decompressed_bytes);
|
||||||
|
archive.unpack(container_dir).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::set_permissions(
|
||||||
|
&binary_path,
|
||||||
|
<fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: binary_path,
|
||||||
|
arguments: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(
|
||||||
|
&self,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
get_cached_server_binary(container_dir)
|
||||||
|
.await
|
||||||
|
.map(|mut binary| {
|
||||||
|
binary.arguments = vec!["--version".into()];
|
||||||
|
binary
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
async_maybe!({
|
||||||
|
let mut last_binary_path = 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_file()
|
||||||
|
&& entry
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.map_or(false, |name| name == "lua-language-server")
|
||||||
|
{
|
||||||
|
last_binary_path = Some(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(path) = last_binary_path {
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path,
|
||||||
|
arguments: Vec::new(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("no cached binary"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
}
|
3
crates/zed2/src/languages/lua/brackets.scm
Normal file
3
crates/zed2/src/languages/lua/brackets.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
("[" @open "]" @close)
|
||||||
|
("{" @open "}" @close)
|
||||||
|
("(" @open ")" @close)
|
10
crates/zed2/src/languages/lua/config.toml
Normal file
10
crates/zed2/src/languages/lua/config.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
name = "Lua"
|
||||||
|
path_suffixes = ["lua"]
|
||||||
|
line_comment = "-- "
|
||||||
|
autoclose_before = ",]}"
|
||||||
|
brackets = [
|
||||||
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
|
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||||
|
]
|
||||||
|
collapsed_placeholder = "--[ ... ]--"
|
10
crates/zed2/src/languages/lua/embedding.scm
Normal file
10
crates/zed2/src/languages/lua/embedding.scm
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
(
|
||||||
|
(comment)* @context
|
||||||
|
.
|
||||||
|
(function_declaration
|
||||||
|
"function" @name
|
||||||
|
name: (_) @name
|
||||||
|
(comment)* @collapse
|
||||||
|
body: (block) @collapse
|
||||||
|
) @item
|
||||||
|
)
|
198
crates/zed2/src/languages/lua/highlights.scm
Normal file
198
crates/zed2/src/languages/lua/highlights.scm
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
;; Keywords
|
||||||
|
|
||||||
|
"return" @keyword
|
||||||
|
|
||||||
|
[
|
||||||
|
"goto"
|
||||||
|
"in"
|
||||||
|
"local"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(break_statement) @keyword
|
||||||
|
|
||||||
|
(do_statement
|
||||||
|
[
|
||||||
|
"do"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(while_statement
|
||||||
|
[
|
||||||
|
"while"
|
||||||
|
"do"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(repeat_statement
|
||||||
|
[
|
||||||
|
"repeat"
|
||||||
|
"until"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(if_statement
|
||||||
|
[
|
||||||
|
"if"
|
||||||
|
"elseif"
|
||||||
|
"else"
|
||||||
|
"then"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(elseif_statement
|
||||||
|
[
|
||||||
|
"elseif"
|
||||||
|
"then"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(else_statement
|
||||||
|
[
|
||||||
|
"else"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(for_statement
|
||||||
|
[
|
||||||
|
"for"
|
||||||
|
"do"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
[
|
||||||
|
"function"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
(function_definition
|
||||||
|
[
|
||||||
|
"function"
|
||||||
|
"end"
|
||||||
|
] @keyword)
|
||||||
|
|
||||||
|
;; Operators
|
||||||
|
|
||||||
|
[
|
||||||
|
"and"
|
||||||
|
"not"
|
||||||
|
"or"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
"+"
|
||||||
|
"-"
|
||||||
|
"*"
|
||||||
|
"/"
|
||||||
|
"%"
|
||||||
|
"^"
|
||||||
|
"#"
|
||||||
|
"=="
|
||||||
|
"~="
|
||||||
|
"<="
|
||||||
|
">="
|
||||||
|
"<"
|
||||||
|
">"
|
||||||
|
"="
|
||||||
|
"&"
|
||||||
|
"~"
|
||||||
|
"|"
|
||||||
|
"<<"
|
||||||
|
">>"
|
||||||
|
"//"
|
||||||
|
".."
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
;; Punctuations
|
||||||
|
|
||||||
|
[
|
||||||
|
";"
|
||||||
|
":"
|
||||||
|
","
|
||||||
|
"."
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
;; Brackets
|
||||||
|
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
;; Variables
|
||||||
|
|
||||||
|
(identifier) @variable
|
||||||
|
|
||||||
|
((identifier) @variable.special
|
||||||
|
(#eq? @variable.special "self"))
|
||||||
|
|
||||||
|
(variable_list
|
||||||
|
attribute: (attribute
|
||||||
|
(["<" ">"] @punctuation.bracket
|
||||||
|
(identifier) @attribute)))
|
||||||
|
|
||||||
|
;; Constants
|
||||||
|
|
||||||
|
((identifier) @constant
|
||||||
|
(#match? @constant "^[A-Z][A-Z_0-9]*$"))
|
||||||
|
|
||||||
|
(vararg_expression) @constant
|
||||||
|
|
||||||
|
(nil) @constant.builtin
|
||||||
|
|
||||||
|
[
|
||||||
|
(false)
|
||||||
|
(true)
|
||||||
|
] @boolean
|
||||||
|
|
||||||
|
;; Tables
|
||||||
|
|
||||||
|
(field name: (identifier) @field)
|
||||||
|
|
||||||
|
(dot_index_expression field: (identifier) @field)
|
||||||
|
|
||||||
|
(table_constructor
|
||||||
|
[
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @constructor)
|
||||||
|
|
||||||
|
;; Functions
|
||||||
|
|
||||||
|
(parameters (identifier) @parameter)
|
||||||
|
|
||||||
|
(function_call
|
||||||
|
name: [
|
||||||
|
(identifier) @function
|
||||||
|
(dot_index_expression field: (identifier) @function)
|
||||||
|
])
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
name: [
|
||||||
|
(identifier) @function.definition
|
||||||
|
(dot_index_expression field: (identifier) @function.definition)
|
||||||
|
])
|
||||||
|
|
||||||
|
(method_index_expression method: (identifier) @method)
|
||||||
|
|
||||||
|
(function_call
|
||||||
|
(identifier) @function.builtin
|
||||||
|
(#any-of? @function.builtin
|
||||||
|
;; built-in functions in Lua 5.1
|
||||||
|
"assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs"
|
||||||
|
"load" "loadfile" "loadstring" "module" "next" "pairs" "pcall" "print"
|
||||||
|
"rawequal" "rawget" "rawset" "require" "select" "setfenv" "setmetatable"
|
||||||
|
"tonumber" "tostring" "type" "unpack" "xpcall"))
|
||||||
|
|
||||||
|
;; Others
|
||||||
|
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
(hash_bang_line) @preproc
|
||||||
|
|
||||||
|
(number) @number
|
||||||
|
|
||||||
|
(string) @string
|
10
crates/zed2/src/languages/lua/indents.scm
Normal file
10
crates/zed2/src/languages/lua/indents.scm
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
(if_statement "end" @end) @indent
|
||||||
|
(do_statement "end" @end) @indent
|
||||||
|
(while_statement "end" @end) @indent
|
||||||
|
(for_statement "end" @end) @indent
|
||||||
|
(repeat_statement "until" @end) @indent
|
||||||
|
(function_declaration "end" @end) @indent
|
||||||
|
|
||||||
|
(_ "[" "]" @end) @indent
|
||||||
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
3
crates/zed2/src/languages/lua/outline.scm
Normal file
3
crates/zed2/src/languages/lua/outline.scm
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
(function_declaration
|
||||||
|
"function" @context
|
||||||
|
name: (_) @name) @item
|
11
crates/zed2/src/languages/markdown/config.toml
Normal file
11
crates/zed2/src/languages/markdown/config.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
name = "Markdown"
|
||||||
|
path_suffixes = ["md", "mdx"]
|
||||||
|
brackets = [
|
||||||
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
|
{ 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 = false, newline = false },
|
||||||
|
]
|
24
crates/zed2/src/languages/markdown/highlights.scm
Normal file
24
crates/zed2/src/languages/markdown/highlights.scm
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
(emphasis) @emphasis
|
||||||
|
(strong_emphasis) @emphasis.strong
|
||||||
|
|
||||||
|
[
|
||||||
|
(atx_heading)
|
||||||
|
(setext_heading)
|
||||||
|
] @title
|
||||||
|
|
||||||
|
[
|
||||||
|
(list_marker_plus)
|
||||||
|
(list_marker_minus)
|
||||||
|
(list_marker_star)
|
||||||
|
(list_marker_dot)
|
||||||
|
(list_marker_parenthesis)
|
||||||
|
] @punctuation.list_marker
|
||||||
|
|
||||||
|
(code_span) @text.literal
|
||||||
|
|
||||||
|
(fenced_code_block
|
||||||
|
(info_string
|
||||||
|
(language) @text.literal))
|
||||||
|
|
||||||
|
(link_destination) @link_uri
|
||||||
|
(link_text) @link_text
|
4
crates/zed2/src/languages/markdown/injections.scm
Normal file
4
crates/zed2/src/languages/markdown/injections.scm
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
(fenced_code_block
|
||||||
|
(info_string
|
||||||
|
(language) @language)
|
||||||
|
(code_fence_content) @content)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue