Detailed changes
@@ -259,6 +259,21 @@ dependencies = [
"futures-lite",
]
+[[package]]
+name = "async-global-executor"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "blocking",
+ "futures-lite",
+ "once_cell",
+]
+
[[package]]
name = "async-io"
version = "1.12.0"
@@ -350,6 +365,32 @@ dependencies = [
"syn",
]
+[[package]]
+name = "async-std"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
+dependencies = [
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils 0.8.14",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "once_cell",
+ "pin-project-lite 0.2.9",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
[[package]]
name = "async-stream"
version = "0.3.3"
@@ -371,6 +412,20 @@ dependencies = [
"syn",
]
+[[package]]
+name = "async-tar"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c49359998a76e32ef6e870dbc079ebad8f1e53e8441c5dd39d27b44493fe331"
+dependencies = [
+ "async-std",
+ "filetime",
+ "libc",
+ "pin-project",
+ "redox_syscall",
+ "xattr",
+]
+
[[package]]
name = "async-task"
version = "4.0.3"
@@ -2080,6 +2135,18 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "filetime"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "redox_syscall",
+ "windows-sys 0.42.0",
+]
+
[[package]]
name = "fixedbitset"
version = "0.4.2"
@@ -2528,6 +2595,18 @@ dependencies = [
"regex",
]
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "go_to_line"
version = "0.1.0"
@@ -3144,6 +3223,15 @@ dependencies = [
"arrayvec 0.7.2",
]
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
[[package]]
name = "language"
version = "0.1.0"
@@ -7017,6 +7105,16 @@ dependencies = [
"tree-sitter",
]
+[[package]]
+name = "tree-sitter-lua"
+version = "0.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d489873fd1a2fa6d5f04930bfc5c081c96f0c038c1437104518b5b842c69b282"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
[[package]]
name = "tree-sitter-markdown"
version = "0.0.1"
@@ -8194,6 +8292,15 @@ dependencies = [
"winapi-build",
]
+[[package]]
+name = "xattr"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "xml-rs"
version = "0.8.4"
@@ -8236,6 +8343,7 @@ dependencies = [
"assets",
"async-compression",
"async-recursion 0.3.2",
+ "async-tar",
"async-trait",
"auto_update",
"backtrace",
@@ -8313,6 +8421,7 @@ dependencies = [
"tree-sitter-go",
"tree-sitter-html",
"tree-sitter-json 0.20.0",
+ "tree-sitter-lua",
"tree-sitter-markdown",
"tree-sitter-python",
"tree-sitter-racket",
@@ -1,4 +1,3 @@
-mod indicator;
pub mod participant;
pub mod room;
@@ -10,22 +9,17 @@ use collections::HashSet;
use postage::watch;
use gpui::{
- actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
- Subscription, Task, ViewHandle, WeakModelHandle,
+ AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
+ Subscription, Task, WeakModelHandle,
};
use project::Project;
-use settings::Settings;
-use indicator::SharingStatusIndicator;
pub use participant::ParticipantLocation;
pub use room::Room;
-actions!(collab, [ToggleScreenSharing]);
-
pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut MutableAppContext) {
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
- cx.add_global_action(toggle_screen_sharing);
}
#[derive(Clone)]
@@ -36,6 +30,7 @@ pub struct IncomingCall {
pub initial_project: Option<proto::ParticipantProject>,
}
+/// Singleton global maintaining the user's participation in a room across workspaces.
pub struct ActiveCall {
room: Option<(ModelHandle<Room>, Vec<Subscription>)>,
location: Option<WeakModelHandle<Project>>,
@@ -46,7 +41,6 @@ pub struct ActiveCall {
),
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
- sharing_status_indicator: Option<(usize, ViewHandle<SharingStatusIndicator>)>,
_subscriptions: Vec<client::Subscription>,
}
@@ -71,7 +65,6 @@ impl ActiveCall {
],
client,
user_store,
- sharing_status_indicator: None,
}
}
@@ -290,8 +283,6 @@ impl ActiveCall {
this.set_room(None, cx).detach_and_log_err(cx);
}
- this.set_sharing_status(room.read(cx).is_screen_sharing(), cx);
-
cx.notify();
}),
cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())),
@@ -316,30 +307,4 @@ impl ActiveCall {
pub fn pending_invites(&self) -> &HashSet<u64> {
&self.pending_invites
}
-
- pub fn set_sharing_status(&mut self, is_screen_sharing: bool, cx: &mut MutableAppContext) {
- if is_screen_sharing {
- if self.sharing_status_indicator.is_none()
- && cx.global::<Settings>().show_call_status_icon
- {
- self.sharing_status_indicator =
- Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
- }
- } else if let Some((window_id, _)) = self.sharing_status_indicator.take() {
- cx.remove_status_bar_item(window_id);
- }
- }
-}
-
-pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut MutableAppContext) {
- if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- let toggle_screen_sharing = room.update(cx, |room, cx| {
- if room.is_screen_sharing() {
- Task::ready(room.unshare_screen(cx))
- } else {
- room.share_screen(cx)
- }
- });
- toggle_screen_sharing.detach_and_log_err(cx);
- }
}
@@ -1,5 +1,5 @@
-use crate::{contact_notification::ContactNotification, contacts_popover};
-use call::{ActiveCall, ParticipantLocation, ToggleScreenSharing};
+use crate::{contact_notification::ContactNotification, contacts_popover, ToggleScreenSharing};
+use call::{ActiveCall, ParticipantLocation};
use client::{proto::PeerId, Authenticate, ContactEventKind, User, UserStore};
use clock::ReplicaId;
use contacts_popover::ContactsPopover;
@@ -6,14 +6,17 @@ mod contacts_popover;
mod incoming_call_notification;
mod notifications;
mod project_shared_notification;
+mod sharing_status_indicator;
use anyhow::anyhow;
use call::ActiveCall;
pub use collab_titlebar_item::{CollabTitlebarItem, ToggleCollaborationMenu};
-use gpui::MutableAppContext;
+use gpui::{actions, MutableAppContext, Task};
use std::sync::Arc;
use workspace::{AppState, JoinProject, ToggleFollow, Workspace};
+actions!(collab, [ToggleScreenSharing]);
+
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
collab_titlebar_item::init(cx);
contact_notification::init(cx);
@@ -22,89 +25,107 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
contacts_popover::init(cx);
incoming_call_notification::init(cx);
project_shared_notification::init(cx);
+ sharing_status_indicator::init(cx);
+ cx.add_global_action(toggle_screen_sharing);
cx.add_global_action(move |action: &JoinProject, cx| {
- let project_id = action.project_id;
- let follow_user_id = action.follow_user_id;
- let app_state = app_state.clone();
- cx.spawn(|mut cx| async move {
- let existing_workspace = cx.update(|cx| {
- cx.window_ids()
- .filter_map(|window_id| cx.root_view::<Workspace>(window_id))
- .find(|workspace| {
- workspace.read(cx).project().read(cx).remote_id() == Some(project_id)
- })
- });
+ join_project(action, app_state.clone(), cx);
+ });
+}
- let workspace = if let Some(existing_workspace) = existing_workspace {
- existing_workspace
+pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut MutableAppContext) {
+ if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
+ let toggle_screen_sharing = room.update(cx, |room, cx| {
+ if room.is_screen_sharing() {
+ Task::ready(room.unshare_screen(cx))
} else {
- let active_call = cx.read(ActiveCall::global);
- let room = active_call
- .read_with(&cx, |call, _| call.room().cloned())
- .ok_or_else(|| anyhow!("not in a call"))?;
- let project = room
- .update(&mut cx, |room, cx| {
- room.join_project(
- project_id,
- app_state.languages.clone(),
- app_state.fs.clone(),
- cx,
- )
- })
- .await?;
+ room.share_screen(cx)
+ }
+ });
+ toggle_screen_sharing.detach_and_log_err(cx);
+ }
+}
- let (_, workspace) = cx.add_window(
- (app_state.build_window_options)(None, None, cx.platform().as_ref()),
- |cx| {
- let mut workspace = Workspace::new(
- Default::default(),
- 0,
- project,
- app_state.dock_default_item_factory,
- cx,
- );
- (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
- workspace
- },
- );
- workspace
- };
+fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut MutableAppContext) {
+ let project_id = action.project_id;
+ let follow_user_id = action.follow_user_id;
+ cx.spawn(|mut cx| async move {
+ let existing_workspace = cx.update(|cx| {
+ cx.window_ids()
+ .filter_map(|window_id| cx.root_view::<Workspace>(window_id))
+ .find(|workspace| {
+ workspace.read(cx).project().read(cx).remote_id() == Some(project_id)
+ })
+ });
- cx.activate_window(workspace.window_id());
- cx.platform().activate(true);
+ let workspace = if let Some(existing_workspace) = existing_workspace {
+ existing_workspace
+ } else {
+ let active_call = cx.read(ActiveCall::global);
+ let room = active_call
+ .read_with(&cx, |call, _| call.room().cloned())
+ .ok_or_else(|| anyhow!("not in a call"))?;
+ let project = room
+ .update(&mut cx, |room, cx| {
+ room.join_project(
+ project_id,
+ app_state.languages.clone(),
+ app_state.fs.clone(),
+ cx,
+ )
+ })
+ .await?;
- workspace.update(&mut cx, |workspace, cx| {
- if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- let follow_peer_id = room
- .read(cx)
- .remote_participants()
- .iter()
- .find(|(_, participant)| participant.user.id == follow_user_id)
- .map(|(_, p)| p.peer_id)
- .or_else(|| {
- // If we couldn't follow the given user, follow the host instead.
- let collaborator = workspace
- .project()
- .read(cx)
- .collaborators()
- .values()
- .find(|collaborator| collaborator.replica_id == 0)?;
- Some(collaborator.peer_id)
- });
+ let (_, workspace) = cx.add_window(
+ (app_state.build_window_options)(None, None, cx.platform().as_ref()),
+ |cx| {
+ let mut workspace = Workspace::new(
+ Default::default(),
+ 0,
+ project,
+ app_state.dock_default_item_factory,
+ cx,
+ );
+ (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
+ workspace
+ },
+ );
+ workspace
+ };
- if let Some(follow_peer_id) = follow_peer_id {
- if !workspace.is_following(follow_peer_id) {
- workspace
- .toggle_follow(&ToggleFollow(follow_peer_id), cx)
- .map(|follow| follow.detach_and_log_err(cx));
- }
+ cx.activate_window(workspace.window_id());
+ cx.platform().activate(true);
+
+ workspace.update(&mut cx, |workspace, cx| {
+ if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
+ let follow_peer_id = room
+ .read(cx)
+ .remote_participants()
+ .iter()
+ .find(|(_, participant)| participant.user.id == follow_user_id)
+ .map(|(_, p)| p.peer_id)
+ .or_else(|| {
+ // If we couldn't follow the given user, follow the host instead.
+ let collaborator = workspace
+ .project()
+ .read(cx)
+ .collaborators()
+ .values()
+ .find(|collaborator| collaborator.replica_id == 0)?;
+ Some(collaborator.peer_id)
+ });
+
+ if let Some(follow_peer_id) = follow_peer_id {
+ if !workspace.is_following(follow_peer_id) {
+ workspace
+ .toggle_follow(&ToggleFollow(follow_peer_id), cx)
+ .map(|follow| follow.detach_and_log_err(cx));
}
}
- });
+ }
+ });
- anyhow::Ok(())
- })
- .detach_and_log_err(cx);
- });
+ anyhow::Ok(())
+ })
+ .detach_and_log_err(cx);
}
@@ -1,11 +1,31 @@
+use call::ActiveCall;
use gpui::{
color::Color,
elements::{MouseEventHandler, Svg},
- Appearance, Element, ElementBox, Entity, MouseButton, RenderContext, View,
+ Appearance, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, View,
};
+use settings::Settings;
use crate::ToggleScreenSharing;
+pub fn init(cx: &mut MutableAppContext) {
+ let active_call = ActiveCall::global(cx);
+
+ let mut status_indicator = None;
+ cx.observe(&active_call, move |call, cx| {
+ if let Some(room) = call.read(cx).room() {
+ if room.read(cx).is_screen_sharing() {
+ if status_indicator.is_none() && cx.global::<Settings>().show_call_status_icon {
+ status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
+ }
+ } else if let Some((window_id, _)) = status_indicator.take() {
+ cx.remove_status_bar_item(window_id);
+ }
+ }
+ })
+ .detach();
+}
+
pub struct SharingStatusIndicator;
impl Entity for SharingStatusIndicator {
@@ -52,8 +52,8 @@ pub fn deploy_context_menu(
AnchorCorner::TopLeft,
vec![
ContextMenuItem::item("Rename Symbol", Rename),
- ContextMenuItem::item("Go To Definition", GoToDefinition),
- ContextMenuItem::item("Go To Type Definition", GoToTypeDefinition),
+ ContextMenuItem::item("Go to Definition", GoToDefinition),
+ ContextMenuItem::item("Go to Type Definition", GoToTypeDefinition),
ContextMenuItem::item("Find All References", FindAllReferences),
ContextMenuItem::item(
"Code Actions",
@@ -910,15 +910,14 @@ impl MutableAppContext {
.map_or(false, |window| window.is_fullscreen)
}
- pub fn window_bounds(&self, window_id: usize) -> WindowBounds {
- self.presenters_and_platform_windows[&window_id].1.bounds()
+ pub fn window_bounds(&self, window_id: usize) -> Option<WindowBounds> {
+ let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
+ Some(window.bounds())
}
pub fn window_display_uuid(&self, window_id: usize) -> Option<Uuid> {
- self.presenters_and_platform_windows[&window_id]
- .1
- .screen()
- .display_uuid()
+ let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
+ window.screen().display_uuid()
}
pub fn render_view(&mut self, params: RenderParams) -> Result<ElementBox> {
@@ -2375,8 +2374,10 @@ impl MutableAppContext {
callback(is_fullscreen, this)
});
- if let Some(uuid) = this.window_display_uuid(window_id) {
- let bounds = this.window_bounds(window_id);
+ if let Some((uuid, bounds)) = this
+ .window_display_uuid(window_id)
+ .zip(this.window_bounds(window_id))
+ {
let mut bounds_observations = this.window_bounds_observations.clone();
bounds_observations.emit(window_id, this, |callback, this| {
callback(bounds, uuid, this)
@@ -2560,8 +2561,10 @@ impl MutableAppContext {
}
fn handle_window_moved(&mut self, window_id: usize) {
- if let Some(display) = self.window_display_uuid(window_id) {
- let bounds = self.window_bounds(window_id);
+ if let Some((display, bounds)) = self
+ .window_display_uuid(window_id)
+ .zip(self.window_bounds(window_id))
+ {
self.window_bounds_observations
.clone()
.emit(window_id, self, move |callback, this| {
@@ -3733,10 +3736,6 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.toggle_window_full_screen(self.window_id)
}
- pub fn window_bounds(&self) -> WindowBounds {
- self.app.window_bounds(self.window_id)
- }
-
pub fn prompt(
&self,
level: PromptLevel,
@@ -1,3 +1,4 @@
+use log::warn;
pub use lsp_types::request::*;
pub use lsp_types::*;
@@ -220,10 +221,10 @@ impl LanguageServer {
}
}
} else {
- return Err(anyhow!(
- "failed to deserialize message:\n{}",
+ warn!(
+ "Failed to deserialize message:\n{}",
std::str::from_utf8(&buffer)?
- ));
+ );
}
// Don't starve the main thread when receiving lots of messages at once.
@@ -11,7 +11,10 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation;
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
use settings::Settings;
-use workspace::{OpenPaths, Workspace, WorkspaceLocation, WORKSPACE_DB};
+use workspace::{
+ notifications::simple_message_notification::MessageNotification, OpenPaths, Workspace,
+ WorkspaceLocation, WORKSPACE_DB,
+};
actions!(projects, [OpenRecent]);
@@ -42,7 +45,7 @@ impl RecentProjectsView {
fn toggle(_: &mut Workspace, _: &OpenRecent, cx: &mut ViewContext<Workspace>) {
cx.spawn(|workspace, mut cx| async move {
- let workspace_locations = cx
+ let workspace_locations: Vec<_> = cx
.background()
.spawn(async {
WORKSPACE_DB
@@ -56,12 +59,20 @@ impl RecentProjectsView {
.await;
workspace.update(&mut cx, |workspace, cx| {
- workspace.toggle_modal(cx, |_, cx| {
- let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
- cx.subscribe(&view, Self::on_event).detach();
- view
- });
- })
+ if !workspace_locations.is_empty() {
+ workspace.toggle_modal(cx, |_, cx| {
+ let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
+ cx.subscribe(&view, Self::on_event).detach();
+ view
+ });
+ } else {
+ workspace.show_notification(0, cx, |cx| {
+ cx.add_view(|_| {
+ MessageNotification::new_message("No recent projects to open.")
+ })
+ })
+ }
+ });
})
.detach();
}
@@ -174,7 +174,7 @@ pub mod simple_message_notification {
}
impl MessageNotification {
- pub fn new_messsage<S: AsRef<str>>(message: S) -> MessageNotification {
+ pub fn new_message<S: AsRef<str>>(message: S) -> MessageNotification {
Self {
message: message.as_ref().to_string(),
click_action: None,
@@ -320,7 +320,7 @@ where
Err(err) => {
workspace.show_notification(0, cx, |cx| {
cx.add_view(|_cx| {
- simple_message_notification::MessageNotification::new_messsage(format!(
+ simple_message_notification::MessageNotification::new_message(format!(
"Error: {:?}",
err,
))
@@ -60,6 +60,7 @@ vim = { path = "../vim" }
workspace = { path = "../workspace" }
anyhow = "1.0.38"
async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
+async-tar = "0.4.2"
async-recursion = "0.3"
async-trait = "0.1"
backtrace = "0.3"
@@ -109,6 +110,7 @@ tree-sitter-ruby = "0.20.0"
tree-sitter-html = "0.19.0"
tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"}
tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a"}
+tree-sitter-lua = "0.0.14"
url = "2.2"
urlencoding = "2.1.2"
uuid = { version = "1.1.2", features = ["v4"] }
@@ -10,6 +10,7 @@ mod html;
mod installation;
mod json;
mod language_plugin;
+mod lua;
mod python;
mod ruby;
mod rust;
@@ -122,6 +123,11 @@ pub fn init(languages: Arc<LanguageRegistry>) {
tree_sitter_racket::language(),
None, //
),
+ (
+ "lua",
+ tree_sitter_lua::language(),
+ Some(Box::new(lua::LuaLspAdapter)),
+ ),
] {
languages.register(name, load_config(name), grammar, lsp_adapter, load_queries);
}
@@ -0,0 +1,108 @@
+use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
+
+use anyhow::{anyhow, bail, Result};
+use async_compression::futures::bufread::GzipDecoder;
+use async_tar::Archive;
+use async_trait::async_trait;
+use client::http::HttpClient;
+use futures::{io::BufReader, StreamExt};
+use language::LanguageServerName;
+use smol::fs;
+use util::{async_iife, ResultExt};
+
+use super::installation::{latest_github_release, GitHubLspBinaryVersion};
+
+#[derive(Copy, Clone)]
+pub struct LuaLspAdapter;
+
+#[async_trait]
+impl super::LspAdapter for LuaLspAdapter {
+ async fn name(&self) -> LanguageServerName {
+ LanguageServerName("lua-language-server".into())
+ }
+
+ async fn server_args(&self) -> Vec<String> {
+ vec![
+ "--logpath=~/lua-language-server.log".into(),
+ "--loglevel=trace".into(),
+ ]
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ http: Arc<dyn HttpClient>,
+ ) -> Result<Box<dyn 'static + Send + Any>> {
+ let release = latest_github_release("LuaLS/lua-language-server", http).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>,
+ http: Arc<dyn HttpClient>,
+ container_dir: PathBuf,
+ ) -> Result<PathBuf> {
+ 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 = http
+ .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(binary_path)
+ }
+
+ async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf> {
+ async_iife!({
+ 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(path)
+ } else {
+ Err(anyhow!("no cached binary"))
+ }
+ })
+ .await
+ .log_err()
+ }
+}
@@ -0,0 +1,3 @@
+("[" @open "]" @close)
+("{" @open "}" @close)
+("(" @open ")" @close)
@@ -0,0 +1,15 @@
+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 },
+]
+
+[overrides.string]
+brackets = [
+{ start = "{", end = "}", close = true, newline = true },
+{ start = "[", end = "]", close = true, newline = true },
+]
@@ -0,0 +1,192 @@
+;; 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
+ (#lua-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.call)
+(function_declaration name: (identifier) @function)
+
+(function_call name: (dot_index_expression field: (identifier) @function.call))
+(function_declaration name: (dot_index_expression field: (identifier) @function))
+
+(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
@@ -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
@@ -0,0 +1,3 @@
+(function_declaration
+ "function" @context
+ name: (_) @name) @item
@@ -293,7 +293,7 @@ pub fn menus() -> Vec<Menu<'static>> {
action: Box::new(editor::GoToTypeDefinition),
},
MenuItem::Action {
- name: "Go to References",
+ name: "Find All References",
action: Box::new(editor::FindAllReferences),
},
MenuItem::Action {