Compare commits

...

1 Commits

Author SHA1 Message Date
Jakub Panek 95f7c53ff9 wip 2024-05-31 16:35:24 +02:00
46 changed files with 1116 additions and 1725 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
max_width = 85
# below requires nightly
# imports_granularity = "Crate"
# group_imports = "StdExternalCrate"
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
Generated
+14
View File
@@ -1243,6 +1243,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "dap"
version = "0.4.1-alpha1"
source = "git+https://github.com/lapce/dap-types?rev=913a2a520416a2a81a1f09a7946f595d5ae3ee4a#913a2a520416a2a81a1f09a7946f595d5ae3ee4a"
dependencies = [
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "darling"
version = "0.14.4"
@@ -2966,6 +2976,7 @@ dependencies = [
"config",
"criterion",
"crossbeam-channel",
"dap",
"directories",
"dmg",
"flate2",
@@ -3054,6 +3065,7 @@ dependencies = [
"clap",
"cocoa",
"crossbeam-channel",
"dap",
"directories",
"dyn-clone",
"flate2",
@@ -3097,6 +3109,7 @@ dependencies = [
"wasi-experimental-http-wasmtime",
"wasmtime",
"wasmtime-wasi",
"windows-sys 0.36.1",
"zstd",
]
@@ -3106,6 +3119,7 @@ version = "0.4.0"
dependencies = [
"anyhow",
"crossbeam-channel",
"dap",
"human-sort",
"indexmap",
"lapce-xi-rope",
+2 -1
View File
@@ -62,6 +62,7 @@ toml = { version = "*" }
toml_edit = { version = "0.20.2", features = ["serde"] }
url = { version = "2.5.0" }
dap-types = { git = "https://github.com/lapce/dap-types", rev = "913a2a520416a2a81a1f09a7946f595d5ae3ee4a", package = "dap" }
lsp-types = { version = "0.95.1", features = ["proposed"] } # not following semver, so should be locked to patch version updates only
psp-types = { git = "https://github.com/lapce/psp-types", rev = "f7fea28f59e7b2d6faa1034a21679ad49b3524ad" }
@@ -105,7 +106,7 @@ version = "0.23.0"
[workspace.dependencies.windows-sys]
version = "0"
features = ["Win32_Foundation"]
features = ["Win32_Foundation", "Win32_System_Threading"]
[profile.release-lto]
inherits = "release"
+4 -4
View File
@@ -11,19 +11,19 @@ name = "task"
program = ""
# the program arguments, e.g. args = ["arg1", "arg2"], optional
# args = []
# arguments = []
# current working directory, optional
# cwd = "${workspace}"
# working_directory = "${workspace}"
# environment variables, optional
# [configs.env]
# [configs.environment]
# VAR1 = "VAL1"
# VAR2 = "VAL2"
# task to run before the run/debug session is started, optional
# [configs.prelaunch]
# program = "cargo"
# args = [
# arguments = [
# "build",
# ]
+1
View File
@@ -25,6 +25,7 @@ lapce-core = { workspace = true }
lapce-proxy = { workspace = true }
lapce-rpc = { workspace = true }
lapce-xi-rope = { workspace = true }
dap-types = { workspace = true }
lsp-types = { workspace = true }
notify = { workspace = true }
once_cell = { workspace = true }
+2 -3
View File
@@ -3932,9 +3932,8 @@ fn panic_hook() {
#[cfg(windows)]
fn error_modal(title: &str, msg: &str) -> i32 {
use std::iter::once;
use std::os::windows::prelude::OsStrExt;
use std::{ffi::OsStr, mem};
use std::{ffi::OsStr, iter::once, mem, os::windows::prelude::OsStrExt};
use windows::Win32::UI::WindowsAndMessaging::{
MessageBoxW, MB_ICONERROR, MB_SYSTEMMODAL,
};
+8 -9
View File
@@ -1,5 +1,7 @@
use std::{path::PathBuf, rc::Rc, sync::Arc};
use dap_types::types::{DapId, RunDebugConfig};
pub use floem::views::editor::command::CommandExecuted;
use floem::{
keyboard::Modifiers, peniko::kurbo::Vec2, views::editor::command::Command,
};
@@ -9,7 +11,6 @@ use lapce_core::command::{
MultiSelectionCommand, ScrollCommand,
};
use lapce_rpc::{
dap_types::{DapId, RunDebugConfig},
plugin::{PluginId, VoltID},
proxy::ProxyStatus,
terminal::{TermId, TerminalProfile},
@@ -30,8 +31,6 @@ use crate::{
workspace::LapceWorkspace,
};
pub use floem::views::editor::command::CommandExecuted;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LapceCommand {
pub kind: CommandKind,
@@ -560,19 +559,19 @@ pub enum InternalCommand {
ReloadFileExplorer,
/// Test whether a file/directory can be created at that path
TestPathCreation {
new_path: PathBuf,
new_path: Url,
},
FinishRenamePath {
current_path: PathBuf,
new_path: PathBuf,
current_path: Url,
new_path: Url,
},
FinishNewNode {
is_dir: bool,
path: PathBuf,
path: Url,
},
FinishDuplicate {
source: PathBuf,
path: PathBuf,
source: Url,
path: Url,
},
GoToLocation {
location: EditorLocation,
+1 -1
View File
@@ -1,10 +1,10 @@
use ::core::slice;
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
};
use ::core::slice;
use floem::peniko::Color;
use itertools::Itertools;
use lapce_core::directory::Directory;
+1 -2
View File
@@ -58,9 +58,8 @@ impl IconThemeConfig {
#[cfg(test)]
mod tests {
use crate::config::icon_theme::try_all_equal_value;
use super::IconThemeConfig;
use crate::config::icon_theme::try_all_equal_value;
#[test]
fn try_all_equal_value_empty_none() {
View File
+5 -8
View File
@@ -6,19 +6,16 @@ use std::{
time::Instant,
};
use dap_types::types::{
self, DapId, RunDebugConfig, SourceBreakpoint, StackFrame, Stopped, ThreadId,
Variable,
};
use floem::{
ext_event::create_ext_action,
reactive::{Memo, RwSignal, Scope},
views::VirtualVector,
};
use lapce_rpc::{
dap_types::{
self, DapId, RunDebugConfig, SourceBreakpoint, StackFrame, Stopped,
ThreadId, Variable,
},
proxy::ProxyResponse,
terminal::TermId,
};
use lapce_rpc::{proxy::ProxyResponse, terminal::TermId};
use serde::{Deserialize, Serialize};
use crate::{
+17 -9
View File
@@ -583,7 +583,7 @@ impl Doc {
if let DocContent::File { path, .. } = self.content.get_untracked() {
self.update_breakpoints(delta, &path, &inval.old_text);
self.common.proxy.update(
path,
Url::from_file_path(path).unwrap(),
delta.clone(),
rev + i as u64 + 1,
);
@@ -799,7 +799,7 @@ impl Doc {
let path =
if let DocContent::File { path, .. } = self.content.get_untracked() {
path
Url::from_file_path(path).unwrap()
} else {
return;
};
@@ -855,7 +855,7 @@ impl Doc {
let path =
if let DocContent::File { path, .. } = self.content.get_untracked() {
path
Url::from_file_path(path).unwrap()
} else {
return;
};
@@ -1159,9 +1159,12 @@ impl Doc {
let path = path.clone();
let proxy = self.common.proxy.clone();
std::thread::spawn(move || {
proxy.get_buffer_head(path, move |result| {
send(result);
});
proxy.get_buffer_head(
Url::from_file_path(path).unwrap(),
move |result| {
send(result);
},
);
});
}
}
@@ -1226,9 +1229,14 @@ impl Doc {
}
});
self.common.proxy.save(rev, path, true, move |result| {
send(result);
})
self.common.proxy.save(
rev,
Url::from_file_path(path).unwrap(),
true,
move |result| {
send(result);
},
)
}
}
+28 -20
View File
@@ -49,7 +49,12 @@ use lsp_types::{
InlineCompletionTriggerKind, Location, MarkedString, MarkupKind, TextEdit,
};
use serde::{Deserialize, Serialize};
use url::Url;
use self::{
diff::DiffInfo,
location::{EditorLocation, EditorPosition},
};
use crate::{
command::{CommandKind, InternalCommand, LapceCommand, LapceWorkbenchCommand},
completion::CompletionStatus,
@@ -69,11 +74,6 @@ use crate::{
window_tab::{CommonData, Focus, WindowTabData},
};
use self::{
diff::DiffInfo,
location::{EditorLocation, EditorPosition},
};
pub mod diff;
pub mod gutter;
pub mod location;
@@ -1133,7 +1133,7 @@ impl EditorData {
} else {
None
} {
Some(path) => path,
Some(path) => Url::from_file_path(path).unwrap(),
None => return,
};
@@ -1353,7 +1353,7 @@ impl EditorData {
} else {
None
} {
Some(path) => path,
Some(path) => Url::from_file_path(path).unwrap(),
None => return,
};
@@ -1395,7 +1395,7 @@ impl EditorData {
.collect()
});
inline_completion.update(|c| {
c.set_items(items, offset, path2);
c.set_items(items, offset, path2.to_file_path().unwrap());
c.update_doc(&doc, offset);
});
},
@@ -1944,7 +1944,7 @@ impl EditorData {
} else {
None
} {
Some(path) => path,
Some(path) => Url::from_file_path(path).unwrap(),
None => return,
};
@@ -2079,9 +2079,12 @@ impl EditorData {
let (tx, rx) = crossbeam_channel::bounded(1);
let proxy = self.common.proxy.clone();
std::thread::spawn(move || {
proxy.get_document_formatting(path, move |result| {
let _ = tx.send(result);
});
proxy.get_document_formatting(
Url::from_file_path(path).unwrap(),
move |result| {
let _ = tx.send(result);
},
);
let result = rx.recv_timeout(std::time::Duration::from_secs(1));
send(result);
});
@@ -2111,9 +2114,12 @@ impl EditorData {
let (tx, rx) = crossbeam_channel::bounded(1);
let proxy = self.common.proxy.clone();
std::thread::spawn(move || {
proxy.get_document_formatting(path, move |result| {
let _ = tx.send(result);
});
proxy.get_document_formatting(
Url::from_file_path(path).unwrap(),
move |result| {
let _ = tx.send(result);
},
);
let result = rx.recv_timeout(std::time::Duration::from_secs(1));
send(result);
});
@@ -2301,11 +2307,13 @@ impl EditorData {
});
}
});
self.common
.proxy
.prepare_rename(path, position, move |result| {
self.common.proxy.prepare_rename(
Url::from_file_path(path).unwrap(),
position,
move |result| {
send(result);
});
},
);
}
pub fn word_at_cursor(&self) -> String {
@@ -2547,7 +2555,7 @@ impl EditorData {
let position = doc
.buffer
.with_untracked(|buffer| buffer.offset_to_position(offset));
let path = match path {
let path = match path.and_then(|d| Url::from_file_path(d).ok()) {
Some(path) => path,
None => return,
};
+1 -2
View File
@@ -18,6 +18,7 @@ use lapce_rpc::{buffer::BufferId, proxy::ProxyResponse};
use lapce_xi_rope::Rope;
use serde::{Deserialize, Serialize};
use super::{EditorData, EditorViewKind};
use crate::{
config::{color::LapceColor, icon::LapceIcons},
doc::{Doc, DocContent},
@@ -27,8 +28,6 @@ use crate::{
window_tab::CommonData,
};
use super::{EditorData, EditorViewKind};
#[derive(Clone)]
pub struct DiffInfo {
pub is_right: bool,
+1 -2
View File
@@ -6,9 +6,8 @@ use floem::{
};
use lapce_core::{buffer::rope_text::RopeText, mode::Mode};
use crate::config::{color::LapceColor, LapceConfig};
use super::{view::changes_colors_screen, EditorData};
use crate::config::{color::LapceColor, LapceConfig};
pub struct EditorGutterView {
id: ViewId,
+31 -36
View File
@@ -25,6 +25,8 @@ use lapce_rpc::{
file::{Duplicating, FileNodeItem, Naming, NamingState, NewNode, Renaming},
proxy::ProxyResponse,
};
use tracing::{event, Level};
use url::Url;
use crate::{
command::{CommandExecuted, CommandKind, InternalCommand, LapceCommand},
@@ -37,10 +39,7 @@ use crate::{
enum RenamedPath {
NotRenaming,
NameUnchanged,
Renamed {
current_path: PathBuf,
new_path: PathBuf,
},
Renamed { current_path: Url, new_path: Url },
}
#[derive(Clone)]
@@ -112,9 +111,13 @@ impl KeyPressFocus for FileExplorerData {
impl FileExplorerData {
pub fn new(cx: Scope, editors: Editors, common: Rc<CommonData>) -> Self {
let path = common.workspace.path.clone().unwrap_or_default();
let path = common
.workspace
.path
.clone()
.and_then(|w| Url::from_directory_path(w).ok());
let root = cx.create_rw_signal(FileNodeItem {
path: path.clone(),
path: path.unwrap_or_default(),
is_dir: true,
read: false,
open: false,
@@ -132,7 +135,7 @@ impl FileExplorerData {
};
if data.common.workspace.path.is_some() {
// only fill in the child files if there is open folder
data.toggle_expand(&path);
data.toggle_expand(&mut &path.unwrap());
}
data
}
@@ -146,7 +149,7 @@ impl FileExplorerData {
/// Toggle whether the directory is expanded or not.
/// Does nothing if the path does not exist or is not a directory.
pub fn toggle_expand(&self, path: &Path) {
pub fn toggle_expand(&self, path: &mut Url) {
let Some(read) = self
.root
.try_update(|root| {
@@ -176,19 +179,18 @@ impl FileExplorerData {
}
}
pub fn read_dir(&self, path: &Path) {
pub fn read_dir(&self, path: &Url) {
self.read_dir_cb(path, |_| {});
}
/// Read the directory's information and update the file explorer tree.
/// `done : FnOnce(was_read: bool)` is called when the operation is completed, whether success,
/// failure, or ignored.
pub fn read_dir_cb(&self, path: &Path, done: impl FnOnce(bool) + 'static) {
pub fn read_dir_cb(&self, path: &Url, done: impl FnOnce(bool) + 'static) {
let root = self.root;
let data = self.clone();
let config = self.common.config;
let send = {
let path = path.to_path_buf();
create_ext_action(self.common.scope, move |result| {
let Ok(ProxyResponse::ReadDirResponse { mut items }) = result else {
done(false);
@@ -198,13 +200,16 @@ impl FileExplorerData {
root.update(|root| {
// Get the node for this path, which should already exist if we're calling
// read_dir on it.
if let Some(node) = root.get_file_node_mut(&path) {
if let Some(node) = root.get_file_node_mut(&mut &path) {
// TODO: do not recreate glob every time we read a directory
// Retain only items that are not excluded from view by the configuration
match Glob::new(&config.get().editor.files_exclude) {
Ok(glob) => {
let matcher = glob.compile_matcher();
items.retain(|i| !matcher.is_match(&i.path));
items.retain(|i| {
!matcher
.is_match(&i.path.to_file_path().unwrap())
});
}
Err(e) => tracing::error!(
target:"files_exclude",
@@ -216,11 +221,10 @@ impl FileExplorerData {
node.read = true;
// Remove paths that no longer exist
let removed_paths: Vec<PathBuf> = node
let removed_paths = node
.children
.keys()
.filter(|p| !items.iter().any(|i| &&i.path == p))
.map(PathBuf::from)
.collect();
for path in removed_paths {
node.children.remove(&path);
@@ -237,7 +241,7 @@ impl FileExplorerData {
}
}
}
root.update_node_count_recursive(&path);
root.update_node_count_recursive(&mut &path);
});
done(true);
@@ -245,20 +249,21 @@ impl FileExplorerData {
};
// Ask the proxy for the directory information
self.common.proxy.read_dir(path.to_path_buf(), send);
self.common.proxy.read_dir(path.clone(), send);
}
/// Returns `true` if `path` exists in the file explorer tree and is a directory, `false`
/// otherwise.
fn is_dir(&self, path: &Path) -> bool {
self.root.with_untracked(|root| {
root.get_file_node(path).is_some_and(|node| node.is_dir)
root.get_file_node(&mut Url::from_directory_path(path.clone()).unwrap())
.is_some_and(|node| node.is_dir)
})
}
/// The current path that we're renaming to / creating or duplicating a node at.
/// Note: returns `None` when renaming if the file name has not changed.
fn naming_path(&self) -> Option<PathBuf> {
fn naming_path(&self) -> Option<Url> {
self.naming.with_untracked(|naming| match naming {
Naming::None => None,
Naming::Renaming(_) => {
@@ -270,24 +275,15 @@ impl FileExplorerData {
}
Naming::NewNode(n) => {
let relative_path = self.naming_editor_data.text();
let relative_path: Cow<OsStr> = match relative_path.slice_to_cow(..)
{
Cow::Borrowed(path) => Cow::Borrowed(path.as_ref()),
Cow::Owned(path) => Cow::Owned(path.into()),
};
Some(n.base_path.join(relative_path))
Some(Url::from_file_path(relative_path).ok()?)
}
Naming::Duplicating(d) => {
let relative_path = self.naming_editor_data.text();
let relative_path: Cow<OsStr> = match relative_path.slice_to_cow(..)
{
Cow::Borrowed(path) => Cow::Borrowed(path.as_ref()),
Cow::Owned(path) => Cow::Owned(path.into()),
};
let new_path =
d.path.parent().unwrap_or("".as_ref()).join(relative_path);
let new_path = lapce_rpc::file::parent(&d.path)
.and_then(|p| p.join(relative_path.to_owned()).ok())
.unwrap();
Some(new_path)
}
@@ -313,10 +309,9 @@ impl FileExplorerData {
if new_relative_path == current_file_name {
RenamedPath::NameUnchanged
} else {
let new_path = current_path
.parent()
.unwrap_or("".as_ref())
.join(new_relative_path);
let new_path = lapce_rpc::file::parent(current_path)
.and_then(|p| p.join(new_relative_path).ok())
.unwrap();
RenamedPath::Renamed {
current_path: current_path.to_owned(),
-1
View File
@@ -9,7 +9,6 @@ use lapce_core::{
rope_text_pos::RopeTextPosition,
selection::Selection,
};
use lsp_types::InsertTextFormat;
use crate::{config::LapceConfig, doc::Doc, editor::EditorData, snippet::Snippet};
+1 -2
View File
@@ -139,9 +139,8 @@ fn get_modes(toml_keymap: &toml_edit::Table) -> Modes {
mod tests {
use floem::keyboard::Key;
use crate::keypress::keymap::KeyMapKey;
use super::*;
use crate::keypress::keymap::KeyMapKey;
#[test]
fn test_keymap() {
+1
View File
@@ -1,4 +1,5 @@
use std::path::PathBuf;
use tracing::{event, Level};
use url::Url;
+1 -2
View File
@@ -755,8 +755,7 @@ impl PaletteData {
#[cfg(windows)]
fn get_wsl_hosts(&self) {
use std::os::windows::process::CommandExt;
use std::process;
use std::{os::windows::process::CommandExt, process};
let cmd = process::Command::new("wsl")
.creation_flags(0x08000000) // CREATE_NO_WINDOW
.arg("-l")
+1 -2
View File
@@ -1,8 +1,7 @@
use std::rc::Rc;
use floem::{
event::EventPropagation,
event::{Event, EventListener},
event::{Event, EventListener, EventPropagation},
kurbo::Size,
reactive::create_rw_signal,
views::{
+13 -1
View File
@@ -2,6 +2,7 @@ use std::{collections::HashMap, path::PathBuf, process::Command, sync::Arc};
use crossbeam_channel::Sender;
use floem::{ext_event::create_signal_from_channel, reactive::ReadSignal};
use itertools::Itertools;
use lapce_proxy::dispatch::Dispatcher;
use lapce_rpc::{
core::{CoreHandler, CoreNotification, CoreRpcHandler},
@@ -58,8 +59,19 @@ pub fn new_proxy(
core_rpc.notification(CoreNotification::ProxyStatus {
status: ProxyStatus::Connecting,
});
let workspace_path = workspace
.path
.clone()
.and_then(|d| Url::from_directory_path(d).ok());
let extra_plugin_paths = extra_plugin_paths
.iter()
.map(|p| Url::from_directory_path(p).unwrap())
.collect_vec();
proxy_rpc.initialize(
workspace.path.clone(),
workspace_path,
disabled_volts,
extra_plugin_paths,
plugin_configurations,
+1 -2
View File
@@ -2,9 +2,8 @@ use std::{path::Path, process::Command};
use anyhow::Result;
use crate::workspace::WslHost;
use super::{new_command, remote::Remote};
use crate::workspace::WslHost;
pub struct WslRemote {
pub wsl: WslHost,
+9 -4
View File
@@ -1,8 +1,13 @@
use alacritty_terminal::grid::Dimensions;
use alacritty_terminal::index::{Column, Direction, Line, Point};
use alacritty_terminal::term::search::{Match, RegexIter, RegexSearch};
use alacritty_terminal::{
event::EventListener, term::test::TermSize, vte::ansi, Term,
event::EventListener,
grid::Dimensions,
index::{Column, Direction, Line, Point},
term::{
search::{Match, RegexIter, RegexSearch},
test::TermSize,
},
vte::ansi,
Term,
};
use crossbeam_channel::Sender;
use lapce_rpc::{proxy::ProxyRpcHandler, terminal::TermId};
+17 -21
View File
@@ -1,27 +1,23 @@
use std::sync::Arc;
use std::time::SystemTime;
use std::{sync::Arc, time::SystemTime};
use alacritty_terminal::index::Side;
use alacritty_terminal::selection::{Selection, SelectionType};
use alacritty_terminal::term::search::Match;
use alacritty_terminal::term::RenderableContent;
use alacritty_terminal::{
grid::Dimensions,
term::{cell::Flags, test::TermSize},
index::Side,
selection::{Selection, SelectionType},
term::{cell::Flags, search::Match, test::TermSize, RenderableContent},
};
use floem::context::{EventCx, PaintCx};
use floem::event::Event;
use floem::peniko::Color;
use floem::pointer::PointerInputEvent;
use floem::views::editor::core::register::Clipboard;
use floem::views::editor::text::SystemClipboard;
use floem::ViewId;
use floem::{
context::{EventCx, PaintCx},
cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout, Weight},
event::EventPropagation,
peniko::kurbo::{Point, Rect, Size},
event::{Event, EventPropagation},
peniko::{
kurbo::{Point, Rect, Size},
Color,
},
pointer::PointerInputEvent,
reactive::{create_effect, ReadSignal, RwSignal},
Renderer, View,
views::editor::{core::register::Clipboard, text::SystemClipboard},
Renderer, View, ViewId,
};
use lapce_core::mode::Mode;
use lapce_rpc::{proxy::ProxyRpcHandler, terminal::TermId};
@@ -30,15 +26,15 @@ use parking_lot::RwLock;
use unicode_width::UnicodeWidthChar;
use super::{panel::TerminalPanelData, raw::RawTerminal};
use crate::command::InternalCommand;
use crate::editor::location::{EditorLocation, EditorPosition};
use crate::listener::Listener;
use crate::workspace::LapceWorkspace;
use crate::{
command::InternalCommand,
config::{color::LapceColor, LapceConfig},
debug::RunDebugProcess,
editor::location::{EditorLocation, EditorPosition},
listener::Listener,
panel::kind::PanelKind,
window_tab::Focus,
workspace::LapceWorkspace,
};
/// Threshold used for double_click/triple_click.
+1 -2
View File
@@ -4,8 +4,7 @@ use floem::{
action::{set_ime_allowed, set_ime_cursor_area},
context::EventCx,
cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout},
event::EventPropagation,
event::{Event, EventListener},
event::{Event, EventListener, EventPropagation},
peniko::{
kurbo::{Line, Point, Rect, Size, Vec2},
Color,
+17 -7
View File
@@ -8,6 +8,7 @@ use std::{
};
use crossbeam_channel::Sender;
use dap_types::types::RunDebugConfig;
use floem::{
action::{open_file, TimerToken},
cosmic_text::{Attrs, AttrsList, FamilyOwned, LineHeightValue, TextLayout},
@@ -27,7 +28,6 @@ use lapce_core::{
};
use lapce_rpc::{
core::CoreNotification,
dap_types::RunDebugConfig,
file::{Naming, PathObject},
proxy::{ProxyResponse, ProxyRpcHandler, ProxyStatus},
source_control::FileDiff,
@@ -451,7 +451,11 @@ impl WindowTabData {
let terminal = TerminalPanelData::new(
workspace.clone(),
common.config.get_untracked().terminal.get_default_profile(),
if workspace.kind.is_local() {
common.config.get_untracked().terminal.get_default_profile()
} else {
None
},
common.clone(),
);
if let Some(workspace_info) = workspace_info.as_ref() {
@@ -941,11 +945,15 @@ impl WindowTabData {
// ==== Terminal ====
NewTerminalTab => {
self.terminal.new_tab(
self.common
if self.workspace.kind.is_local() {
self.common
.config
.get_untracked()
.terminal
.get_default_profile(),
.get_default_profile()
} else {
None
},
);
if !self.panel.is_panel_visible(&PanelKind::Terminal) {
self.panel.show_panel(&PanelKind::Terminal);
@@ -2330,13 +2338,15 @@ impl WindowTabData {
.tab_info
.with_untracked(|info| info.tabs.is_empty())
{
self.terminal.new_tab(
self.terminal.new_tab(if self.workspace.kind.is_local() {
self.common
.config
.get_untracked()
.terminal
.get_default_profile(),
);
.get_default_profile()
} else {
None
});
}
self.panel.show_panel(&kind);
if kind == PanelKind::Search
+1 -3
View File
@@ -35,13 +35,11 @@ use self::{
util::RopeProvider,
};
use crate::{
buffer::rope_text::RopeText,
buffer::{rope_text::RopeText, Buffer},
language::{self, LapceLanguage},
lens::{Lens, LensBuilder},
style::SCOPES,
};
use crate::buffer::Buffer;
pub mod edit;
pub mod highlight;
pub mod util;
+4
View File
@@ -32,6 +32,7 @@ tracing = { workspace = true }
tracing-log = { workspace = true }
url = { workspace = true }
dap-types = { workspace = true }
lsp-types = { workspace = true }
psp-types = { workspace = true }
@@ -71,6 +72,9 @@ wasi-common = "14.0.0"
git = "https://github.com/lapce/wasi-experimental-http"
# path = "../../wasi-experimental-http/crates/wasi-experimental-http-wasmtime"
[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = {workspace = true}
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25"
objc = "0.2"
+67 -335
View File
@@ -13,8 +13,9 @@ use std::{
use alacritty_terminal::{event::WindowSize, event_loop::Msg};
use anyhow::{anyhow, Context, Result};
use crossbeam_channel::Sender;
use git2::ErrorCode::NotFound;
use git2::{build::CheckoutBuilder, DiffOptions, Oid, Repository};
use git2::{
build::CheckoutBuilder, DiffOptions, ErrorCode::NotFound, Oid, Repository,
};
use grep_matcher::Matcher;
use grep_regex::RegexMatcherBuilder;
use grep_searcher::{sinks::UTF8, SearcherBuilder};
@@ -41,6 +42,7 @@ use crate::{
buffer::{get_mod_time, load_file, Buffer},
plugin::{catalog::PluginCatalog, PluginCatalogRpcHandler},
terminal::{Terminal, TerminalSender},
vcs,
watcher::{FileWatcher, Notify, WatchToken},
};
@@ -48,11 +50,12 @@ const OPEN_FILE_EVENT_TOKEN: WatchToken = WatchToken(1);
const WORKSPACE_EVENT_TOKEN: WatchToken = WatchToken(2);
pub struct Dispatcher {
workspace: Option<PathBuf>,
workspace: Option<Url>,
repository: Option<vcs::Repository>,
pub proxy_rpc: ProxyRpcHandler,
core_rpc: CoreRpcHandler,
catalog_rpc: PluginCatalogRpcHandler,
buffers: HashMap<PathBuf, Buffer>,
buffers: HashMap<Url, Buffer>,
terminals: HashMap<TermId, TerminalSender>,
file_watcher: FileWatcher,
window_id: usize,
@@ -74,14 +77,19 @@ impl ProxyHandler for Dispatcher {
self.window_id = window_id;
self.tab_id = tab_id;
self.workspace = workspace;
let workspace_path =
self.workspace.as_ref().and_then(|w| w.to_file_path().ok());
self.file_watcher.notify(FileWatchNotifier::new(
self.workspace.clone(),
workspace_path,
self.core_rpc.clone(),
self.proxy_rpc.clone(),
));
if let Some(workspace) = self.workspace.as_ref() {
if let Some(workspace) = workspace_path {
self.file_watcher
.watch(workspace, true, WORKSPACE_EVENT_TOKEN);
.watch(&workspace, true, WORKSPACE_EVENT_TOKEN);
}
let plugin_rpc = self.catalog_rpc.clone();
@@ -282,8 +290,8 @@ impl ProxyHandler for Dispatcher {
let _ = self.catalog_rpc.enable_volt(volt);
}
GitCommit { message, diffs } => {
if let Some(workspace) = self.workspace.as_ref() {
match git_commit(workspace, &message, diffs) {
if let Some(repo) = self.repository.as_ref() {
match repo.commit(&message, diffs) {
Ok(()) => (),
Err(e) => {
self.core_rpc.show_message(
@@ -298,18 +306,17 @@ impl ProxyHandler for Dispatcher {
}
}
GitCheckout { reference } => {
if let Some(workspace) = self.workspace.as_ref() {
match git_checkout(workspace, &reference) {
if let Some(repo) = self.repository.as_ref() {
match repo.checkout(&reference) {
Ok(()) => (),
Err(e) => eprintln!("{e:?}"),
}
}
}
GitDiscardFilesChanges { files } => {
if let Some(workspace) = self.workspace.as_ref() {
match git_discard_files_changes(
workspace,
files.iter().map(AsRef::as_ref),
if let Some(repo) = self.repository.as_ref() {
match repo.discard_files_changes(
files.iter().map(|f| &f.to_file_path().unwrap()),
) {
Ok(()) => (),
Err(e) => eprintln!("{e:?}"),
@@ -317,8 +324,8 @@ impl ProxyHandler for Dispatcher {
}
}
GitDiscardWorkspaceChanges {} => {
if let Some(workspace) = self.workspace.as_ref() {
match git_discard_workspace_changes(workspace) {
if let Some(repo) = self.repository.as_ref() {
match repo.discard_workspace_changes() {
Ok(()) => (),
Err(e) => eprintln!("{e:?}"),
}
@@ -326,9 +333,11 @@ impl ProxyHandler for Dispatcher {
}
GitInit {} => {
if let Some(workspace) = self.workspace.as_ref() {
match git_init(workspace) {
Ok(()) => (),
Err(e) => eprintln!("{e:?}"),
if let Ok(workspace) = workspace.to_file_path() {
match vcs::Repository::discover_or_init(&workspace) {
Ok(r) => self.repository = Some(r),
Err(e) => eprintln!("{e:?}"),
}
}
}
}
@@ -734,13 +743,26 @@ impl ProxyHandler for Dispatcher {
.into_iter()
.filter_map(|entry| {
entry
.map(|e| FileNodeItem {
path: e.path(),
is_dir: e.path().is_dir(),
open: false,
read: false,
children: HashMap::new(),
children_open_count: 0,
.map(|e| {
let is_dir = e.path().is_dir();
FileNodeItem {
path: if is_dir {
url::Url::from_directory_path(
e.path(),
)
.unwrap()
} else {
url::Url::from_file_path(
e.path(),
)
.unwrap()
},
is_dir,
open: false,
read: false,
children: HashMap::new(),
children_open_count: 0,
}
})
.ok()
})
@@ -946,10 +968,14 @@ impl ProxyHandler for Dispatcher {
// `path` or rename an item to `path` will succeed.
// Currently the only conditions that are tested are that `path` doesn't already
// exist and that `path` doesn't have a parent that exists and is not a directory.
let result = if path.exists() {
Err(format!("{} already exists", path.display()))
let result = if let Ok(path) = path.to_file_path() {
if path.exists() {
Err(format!("{} already exists", path.display()))
} else {
Ok(path)
}
} else {
Ok(path)
Err(format!("Failed to convert `{path}` to file path"))
};
let result = result
@@ -976,7 +1002,7 @@ impl ProxyHandler for Dispatcher {
GetSelectionRange { positions, path } => {
let proxy_rpc = self.proxy_rpc.clone();
self.catalog_rpc.get_selection_range(
path.as_path(),
&path,
positions,
move |_, result| {
let result = result.map(|ranges| {
@@ -1112,6 +1138,12 @@ impl FileWatchNotifier {
fn handle_open_file_fs_event(&self, event: notify::Event) {
if event.kind.is_modify() {
for path in event.paths {
let path = if path.is_dir() {
Url::from_directory_path(path)
} else {
Url::from_file_path(path)
}
.unwrap();
self.proxy_rpc
.notification(ProxyNotification::OpenFileChanged { path });
}
@@ -1174,309 +1206,6 @@ impl FileWatchNotifier {
}
}
#[derive(Clone, Debug)]
pub struct DiffHunk {
pub old_start: u32,
pub old_lines: u32,
pub new_start: u32,
pub new_lines: u32,
pub header: String,
}
fn git_init(workspace_path: &Path) -> Result<()> {
if Repository::discover(workspace_path).is_err() {
Repository::init(workspace_path)?;
};
Ok(())
}
fn git_commit(
workspace_path: &Path,
message: &str,
diffs: Vec<FileDiff>,
) -> Result<()> {
let repo = Repository::discover(workspace_path)?;
let mut index = repo.index()?;
for diff in diffs {
match diff {
FileDiff::Modified(p) | FileDiff::Added(p) => {
index.add_path(p.strip_prefix(workspace_path)?)?;
}
FileDiff::Renamed(a, d) => {
index.add_path(a.strip_prefix(workspace_path)?)?;
index.remove_path(d.strip_prefix(workspace_path)?)?;
}
FileDiff::Deleted(p) => {
index.remove_path(p.strip_prefix(workspace_path)?)?;
}
}
}
index.write()?;
let tree = index.write_tree()?;
let tree = repo.find_tree(tree)?;
match repo.signature() {
Ok(signature) => {
let parents = repo
.head()
.and_then(|head| Ok(vec![head.peel_to_commit()?]))
.unwrap_or(vec![]);
let parents_refs = parents.iter().collect::<Vec<_>>();
repo.commit(
Some("HEAD"),
&signature,
&signature,
message,
&tree,
&parents_refs,
)?;
Ok(())
}
Err(e) => match e.code() {
NotFound => Err(anyhow!(
"No user.name and/or user.email configured for this git repository."
)),
_ => Err(anyhow!(
"Error while creating commit's signature: {}",
e.message()
)),
},
}
}
fn git_checkout(workspace_path: &Path, reference: &str) -> Result<()> {
let repo = Repository::discover(workspace_path)?;
let (object, reference) = repo.revparse_ext(reference)?;
repo.checkout_tree(&object, None)?;
repo.set_head(reference.unwrap().name().unwrap())?;
Ok(())
}
fn git_discard_files_changes<'a>(
workspace_path: &Path,
files: impl Iterator<Item = &'a Path>,
) -> Result<()> {
let repo = Repository::discover(workspace_path)?;
let mut checkout_b = CheckoutBuilder::new();
checkout_b.update_only(false).force();
let mut had_path = false;
for path in files {
// Remove the workspace path so it is relative to the folder
if let Ok(path) = path.strip_prefix(workspace_path) {
had_path = true;
checkout_b.path(path);
}
}
if !had_path {
// If there we no paths then we do nothing
// because the default behavior of checkout builder is to select all files
// if it is not given a path
return Ok(());
}
repo.checkout_index(None, Some(&mut checkout_b))?;
Ok(())
}
fn git_discard_workspace_changes(workspace_path: &Path) -> Result<()> {
let repo = Repository::discover(workspace_path)?;
let mut checkout_b = CheckoutBuilder::new();
checkout_b.force();
repo.checkout_index(None, Some(&mut checkout_b))?;
Ok(())
}
fn git_delta_format(
workspace_path: &Path,
delta: &git2::DiffDelta,
) -> Option<(git2::Delta, git2::Oid, PathBuf)> {
match delta.status() {
git2::Delta::Added | git2::Delta::Untracked => Some((
git2::Delta::Added,
delta.new_file().id(),
delta.new_file().path().map(|p| workspace_path.join(p))?,
)),
git2::Delta::Deleted => Some((
git2::Delta::Deleted,
delta.old_file().id(),
delta.old_file().path().map(|p| workspace_path.join(p))?,
)),
git2::Delta::Modified => Some((
git2::Delta::Modified,
delta.new_file().id(),
delta.new_file().path().map(|p| workspace_path.join(p))?,
)),
_ => None,
}
}
fn git_diff_new(workspace_path: &Path) -> Option<DiffInfo> {
let repo = Repository::discover(workspace_path).ok()?;
let name = match repo.head() {
Ok(head) => head.shorthand()?.to_string(),
_ => "(No branch)".to_owned(),
};
let mut branches = Vec::new();
for branch in repo.branches(None).ok()? {
branches.push(branch.ok()?.0.name().ok()??.to_string());
}
let mut tags = Vec::new();
if let Ok(git_tags) = repo.tag_names(None) {
for tag in git_tags.into_iter().flatten() {
tags.push(tag.to_owned());
}
}
let mut deltas = Vec::new();
let mut diff_options = DiffOptions::new();
let diff = repo
.diff_index_to_workdir(
None,
Some(
diff_options
.include_untracked(true)
.recurse_untracked_dirs(true),
),
)
.ok()?;
for delta in diff.deltas() {
if let Some(delta) = git_delta_format(workspace_path, &delta) {
deltas.push(delta);
}
}
let oid = match repo.revparse_single("HEAD^{tree}") {
Ok(obj) => obj.id(),
_ => Oid::zero(),
};
let cached_diff = repo
.diff_tree_to_index(repo.find_tree(oid).ok().as_ref(), None, None)
.ok();
if let Some(cached_diff) = cached_diff {
for delta in cached_diff.deltas() {
if let Some(delta) = git_delta_format(workspace_path, &delta) {
deltas.push(delta);
}
}
}
let mut renames = Vec::new();
let mut renamed_deltas = HashSet::new();
for (added_index, delta) in deltas.iter().enumerate() {
if delta.0 == git2::Delta::Added {
for (deleted_index, d) in deltas.iter().enumerate() {
if d.0 == git2::Delta::Deleted && d.1 == delta.1 {
renames.push((added_index, deleted_index));
renamed_deltas.insert(added_index);
renamed_deltas.insert(deleted_index);
break;
}
}
}
}
let mut file_diffs = Vec::new();
for (added_index, deleted_index) in renames.iter() {
file_diffs.push(FileDiff::Renamed(
deltas[*added_index].2.clone(),
deltas[*deleted_index].2.clone(),
));
}
for (i, delta) in deltas.iter().enumerate() {
if renamed_deltas.contains(&i) {
continue;
}
let diff = match delta.0 {
git2::Delta::Added => FileDiff::Added(delta.2.clone()),
git2::Delta::Deleted => FileDiff::Deleted(delta.2.clone()),
git2::Delta::Modified => FileDiff::Modified(delta.2.clone()),
_ => continue,
};
file_diffs.push(diff);
}
file_diffs.sort_by_key(|d| match d {
FileDiff::Modified(p)
| FileDiff::Added(p)
| FileDiff::Renamed(p, _)
| FileDiff::Deleted(p) => p.clone(),
});
Some(DiffInfo {
head: name,
branches,
tags,
diffs: file_diffs,
})
}
fn file_get_head(workspace_path: &Path, path: &Path) -> Result<(String, String)> {
let repo = Repository::discover(workspace_path)?;
let head = repo.head()?;
let tree = head.peel_to_tree()?;
let tree_entry = tree.get_path(path.strip_prefix(workspace_path)?)?;
let blob = repo.find_blob(tree_entry.id())?;
let id = blob.id().to_string();
let content = std::str::from_utf8(blob.content())
.with_context(|| "content bytes to string")?
.to_string();
Ok((id, content))
}
fn git_get_remote_file_url(workspace_path: &Path, file: &Path) -> Result<String> {
let repo = Repository::discover(workspace_path)?;
let head = repo.head()?;
let target_remote = repo.find_remote(
repo.branch_upstream_remote(head.name().unwrap())?
.as_str()
.unwrap(),
)?;
// Grab URL part of remote
let remote = target_remote
.url()
.ok_or(anyhow!("Failed to convert remote to str"))?;
let remote_url = match Url::parse(remote) {
Ok(url) => url,
Err(_) => {
// Parse URL as ssh
Url::parse(&format!("ssh://{}", remote.replacen(':', "/", 1)))?
}
};
// Get host part
let host = remote_url
.host_str()
.ok_or(anyhow!("Couldn't find remote host"))?;
// Get namespace (e.g. organisation/project in case of GitHub, org/team/team/team/../project on GitLab)
let namespace = if let Some(stripped) = remote_url.path().strip_suffix(".git") {
stripped
} else {
remote_url.path()
};
let commit = head.peel_to_commit()?.id();
let file_path = file
.strip_prefix(workspace_path)?
.to_str()
.ok_or(anyhow!("Couldn't convert file path to str"))?;
let url = format!("https://{host}{namespace}/blob/{commit}/{file_path}",);
Ok(url)
}
fn search_in_path(
id: u64,
current_id: &AtomicU64,
@@ -1551,7 +1280,10 @@ fn search_in_path(
}),
);
if !line_matches.is_empty() {
matches.insert(path.clone(), line_matches);
matches.insert(
Url::from_file_path(&path.clone()).unwrap(),
line_matches,
);
}
}
}
+1
View File
@@ -5,6 +5,7 @@ pub mod cli;
pub mod dispatch;
pub mod plugin;
pub mod terminal;
pub mod vcs;
pub mod watcher;
use std::{
+26 -25
View File
@@ -9,23 +9,23 @@ use std::{
thread,
};
use lapce_rpc::plugin::VoltInfo;
use lapce_rpc::{
dap_types::{self, DapId, DapServer, SetBreakpointsResponse},
plugin::{PluginId, VoltID, VoltMetadata},
dap::{DapId, DapServer},
plugin::{PluginId, VoltID, VoltInfo, VoltMetadata},
proxy::ProxyResponse,
style::LineStyle,
RpcError,
};
use lapce_xi_rope::{Rope, RopeDelta};
use lsp_types::request::Request;
use lsp_types::{
notification::DidOpenTextDocument, DidOpenTextDocumentParams, SemanticTokens,
TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier,
notification::DidOpenTextDocument, request::Request, DidOpenTextDocumentParams,
SemanticTokens, TextDocumentIdentifier, TextDocumentItem,
VersionedTextDocumentIdentifier,
};
use parking_lot::Mutex;
use psp_types::Notification;
use serde_json::Value;
use url::Url;
use super::{
dap::{DapClient, DapRpcHandler, DebuggerData},
@@ -38,21 +38,21 @@ use crate::plugin::{
};
pub struct PluginCatalog {
workspace: Option<PathBuf>,
workspace: Option<Url>,
plugin_rpc: PluginCatalogRpcHandler,
plugins: HashMap<PluginId, PluginServerRpcHandler>,
daps: HashMap<DapId, DapRpcHandler>,
debuggers: HashMap<String, DebuggerData>,
plugin_configurations: HashMap<String, HashMap<String, serde_json::Value>>,
unactivated_volts: HashMap<VoltID, VoltMetadata>,
open_files: HashMap<PathBuf, String>,
open_files: HashMap<Url, String>,
}
impl PluginCatalog {
pub fn new(
workspace: Option<PathBuf>,
workspace: Option<Url>,
disabled_volts: Vec<VoltID>,
extra_plugin_paths: Vec<PathBuf>,
extra_plugin_paths: Vec<Url>,
plugin_configurations: HashMap<String, HashMap<String, serde_json::Value>>,
plugin_rpc: PluginCatalogRpcHandler,
) -> Self {
@@ -82,7 +82,7 @@ impl PluginCatalog {
method: Cow<'static, str>,
params: Value,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
f: Box<dyn ClonableCallback<Value, RpcError>>,
) {
@@ -153,7 +153,7 @@ impl PluginCatalog {
method: impl Into<Cow<'static, str>>,
params: Value,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
) {
if let Some(plugin_id) = plugin_id {
@@ -249,9 +249,11 @@ impl PluginCatalog {
}
if let Ok(matcher) = builder.build() {
if !matcher.is_empty() {
for entry in walkdir::WalkDir::new(workspace)
.into_iter()
.flatten()
for entry in walkdir::WalkDir::new(
workspace.to_file_path().unwrap(),
)
.into_iter()
.flatten()
{
if matcher.is_match(entry.path()) {
return Some(id.clone());
@@ -269,9 +271,9 @@ impl PluginCatalog {
}
pub fn handle_did_open_text_document(&mut self, document: TextDocumentItem) {
if let Ok(path) = document.uri.to_file_path() {
self.open_files.insert(path, document.language_id.clone());
}
let path = document.uri.clone();
self.open_files
.insert(path.clone(), document.language_id.clone());
let to_be_activated: Vec<VoltID> = self
.unactivated_volts
@@ -291,7 +293,6 @@ impl PluginCatalog {
.collect();
self.start_unactivated_volts(to_be_activated);
let path = document.uri.to_file_path().ok();
for (_, plugin) in self.plugins.iter() {
plugin.server_notification(
DidOpenTextDocument::METHOD,
@@ -299,7 +300,7 @@ impl PluginCatalog {
text_document: document.clone(),
},
Some(document.language_id.clone()),
path.clone(),
Some(path.clone()),
true,
);
}
@@ -308,7 +309,7 @@ impl PluginCatalog {
pub fn handle_did_save_text_document(
&mut self,
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: Rope,
) {
@@ -478,14 +479,14 @@ impl PluginCatalog {
{
for item in items {
let language_id = Some(item.language_id.clone());
let path = item.uri.to_file_path().ok();
let path = item.uri.clone();
plugin.server_notification(
DidOpenTextDocument::METHOD,
DidOpenTextDocumentParams {
text_document: item,
},
language_id,
path,
Some(path),
true,
);
}
@@ -571,8 +572,8 @@ impl PluginCatalog {
if let Ok(dap_rpc) = DapClient::start(
DapServer {
program: debugger.program,
args: debugger.args.unwrap_or_default(),
cwd: workspace,
arguments: debugger.args.unwrap_or_default(),
working_directory: workspace,
},
config.clone(),
breakpoints,
+3 -14
View File
@@ -13,18 +13,7 @@ use std::{
use anyhow::{anyhow, Result};
use crossbeam_channel::{Receiver, Sender};
use lapce_rpc::{
dap_types::{
self, ConfigurationDone, Continue, ContinueArguments, ContinueResponse,
DapEvent, DapId, DapPayload, DapRequest, DapResponse, DapServer,
DebuggerCapabilities, Disconnect, Initialize, Launch, Next, NextArguments,
Pause, PauseArguments, Request, RunDebugConfig, RunInTerminal,
RunInTerminalArguments, RunInTerminalResponse, Scope, Scopes,
ScopesArguments, ScopesResponse, SetBreakpoints, SetBreakpointsArguments,
SetBreakpointsResponse, Source, SourceBreakpoint, StackTrace,
StackTraceArguments, StackTraceResponse, StepIn, StepInArguments, StepOut,
StepOutArguments, Terminate, ThreadId, Threads, ThreadsResponse, Variable,
Variables, VariablesArguments, VariablesResponse,
},
dap::{DapId, RunDebugConfig, ThreadId},
terminal::TermId,
RpcError,
};
@@ -590,8 +579,8 @@ impl DapRpcHandler {
pub fn launch(&self, config: &RunDebugConfig) -> Result<()> {
let params = serde_json::json!({
"program": config.program,
"args": config.args,
"cwd": config.cwd,
"args": config.arguments,
"cwd": config.working_directory,
"runInTerminal": true,
});
let _resp = self
+21 -17
View File
@@ -1,8 +1,5 @@
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
use std::{
io::{BufRead, BufReader, BufWriter, Write},
path::{Path, PathBuf},
process::{self, Child, Command, Stdio},
sync::Arc,
thread,
@@ -61,7 +58,7 @@ pub struct LspClient {
plugin_rpc: PluginCatalogRpcHandler,
server_rpc: PluginServerRpcHandler,
process: Child,
workspace: Option<PathBuf>,
workspace: Option<Url>,
host: PluginHostHandler,
options: Option<Value>,
}
@@ -74,7 +71,7 @@ impl PluginServerHandler for LspClient {
fn document_supported(
&mut self,
lanaguage_id: Option<&str>,
path: Option<&Path>,
path: Option<&Url>,
) -> bool {
self.host.document_supported(lanaguage_id, path)
}
@@ -115,7 +112,7 @@ impl PluginServerHandler for LspClient {
fn handle_did_save_text_document(
&self,
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: lapce_xi_rope::Rope,
) {
@@ -166,12 +163,12 @@ impl LspClient {
fn new(
plugin_rpc: PluginCatalogRpcHandler,
document_selector: DocumentSelector,
workspace: Option<PathBuf>,
workspace: Option<Url>,
volt_id: VoltID,
volt_display_name: String,
spawned_by: Option<PluginId>,
plugin_id: Option<PluginId>,
pwd: Option<PathBuf>,
pwd: Option<Url>,
server_uri: Url,
args: Vec<String>,
options: Option<Value>,
@@ -303,12 +300,12 @@ impl LspClient {
pub fn start(
plugin_rpc: PluginCatalogRpcHandler,
document_selector: DocumentSelector,
workspace: Option<PathBuf>,
workspace: Option<Url>,
volt_id: VoltID,
volt_display_name: String,
spawned_by: Option<PluginId>,
plugin_id: Option<PluginId>,
pwd: Option<PathBuf>,
pwd: Option<Url>,
server_uri: Url,
args: Vec<String>,
options: Option<Value>,
@@ -336,10 +333,8 @@ impl LspClient {
}
fn initialize(&mut self) {
let root_uri = self
.workspace
.clone()
.map(|p| Url::from_directory_path(p).unwrap());
let root_uri = self.workspace.clone();
#[allow(deprecated)]
let params = InitializeParams {
process_id: Some(process::id()),
@@ -404,19 +399,28 @@ impl LspClient {
}
fn process(
workspace: Option<&PathBuf>,
workspace: Option<&Url>,
server: &str,
args: &[String],
) -> Result<Child> {
let mut process = Command::new(server);
if let Some(workspace) = workspace {
process.current_dir(workspace);
if let Ok(workspace) = workspace.to_file_path() {
process.current_dir(workspace);
}
}
process.args(args);
#[cfg(target_os = "windows")]
let process = process.creation_flags(0x08000000);
let process = {
use std::os::windows::process::CommandExt;
process.creation_flags(
windows_sys::Win32::System::Threading::CREATE_NO_WINDOW,
)
};
let child = process
.stdin(Stdio::piped())
.stdout(Stdio::piped())
+149 -122
View File
@@ -23,7 +23,8 @@ use flate2::read::GzDecoder;
use lapce_core::directory::Directory;
use lapce_rpc::{
core::CoreRpcHandler,
dap_types::{self, DapId, RunDebugConfig, SourceBreakpoint, ThreadId},
dap::{Breakpoints, BreakpointsMap},
dap_types::{self, DapId, RunDebugConfig, ThreadId},
plugin::{PluginId, VoltInfo, VoltMetadata},
proxy::ProxyRpcHandler,
style::LineStyle,
@@ -89,7 +90,7 @@ pub enum PluginCatalogRpc {
method: Cow<'static, str>,
params: Value,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
f: Box<dyn ClonableCallback<Value, RpcError>>,
},
@@ -98,7 +99,7 @@ pub enum PluginCatalogRpc {
method: Cow<'static, str>,
params: Value,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
},
FormatSemanticTokens {
@@ -134,7 +135,7 @@ pub enum PluginCatalogRpc {
},
DidSaveTextDocument {
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: Rope,
},
@@ -159,7 +160,7 @@ pub enum PluginCatalogNotification {
DapDisconnected(DapId),
DapStart {
config: RunDebugConfig,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: BreakpointsMap,
},
DapProcessId {
dap_id: DapId,
@@ -194,12 +195,12 @@ pub enum PluginCatalogNotification {
},
DapRestart {
dap_id: DapId,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: BreakpointsMap,
},
DapSetBreakpoints {
dap_id: DapId,
path: PathBuf,
breakpoints: Vec<SourceBreakpoint>,
path: Url,
breakpoints: Breakpoints,
},
RegisterDebuggerType {
debugger_type: String,
@@ -369,7 +370,7 @@ impl PluginCatalogRpcHandler {
method: &'static str,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
cb: impl FnOnce(PluginId, Result<Resp, RpcError>) + Clone + Send + 'static,
) where
P: Serialize,
@@ -424,7 +425,7 @@ impl PluginCatalogRpcHandler {
method: impl Into<Cow<'static, str>>,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
f: impl FnOnce(PluginId, Result<Value, RpcError>) + Send + DynClone + 'static,
) {
@@ -449,7 +450,7 @@ impl PluginCatalogRpcHandler {
method: impl Into<Cow<'static, str>>,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
) {
let params = serde_json::to_value(params).unwrap();
@@ -479,31 +480,32 @@ impl PluginCatalogRpcHandler {
});
}
pub fn did_save_text_document(&self, path: &Path, text: Rope) {
let text_document =
TextDocumentIdentifier::new(Url::from_file_path(path).unwrap());
let language_id = language_id_from_path(path).unwrap_or("").to_string();
pub fn did_save_text_document(&self, path: &Url, text: Rope) {
let text_document = TextDocumentIdentifier::new(path.clone());
let language_id = language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string();
let _ = self.plugin_tx.send(PluginCatalogRpc::DidSaveTextDocument {
language_id,
text_document,
path: path.into(),
path: path.clone(),
text,
});
}
pub fn did_change_text_document(
&self,
path: &Path,
path: &Url,
rev: u64,
delta: RopeDelta,
text: Rope,
new_text: Rope,
) {
let document = VersionedTextDocumentIdentifier::new(
Url::from_file_path(path).unwrap(),
rev as i32,
);
let language_id = language_id_from_path(path).unwrap_or("").to_string();
let document =
VersionedTextDocumentIdentifier::new(path.clone(), rev as i32);
let language_id = language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string();
let _ = self
.plugin_tx
.send(PluginCatalogRpc::DidChangeTextDocument {
@@ -517,80 +519,83 @@ impl PluginCatalogRpcHandler {
pub fn get_definition(
&self,
path: &Path,
path: &Url,
position: Position,
cb: impl FnOnce(PluginId, Result<GotoDefinitionResponse, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = GotoDefinition::METHOD;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_type_definition(
&self,
path: &Path,
path: &Url,
position: Position,
cb: impl FnOnce(PluginId, Result<GotoTypeDefinitionResponse, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = GotoTypeDefinition::METHOD;
let params = GotoTypeDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_references(
&self,
path: &Path,
path: &Url,
position: Position,
cb: impl FnOnce(PluginId, Result<Vec<Location>, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = References::METHOD;
let params = ReferenceParams {
text_document_position: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
work_done_progress_params: WorkDoneProgressParams::default(),
@@ -600,20 +605,23 @@ impl PluginCatalogRpcHandler {
},
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_code_actions(
&self,
path: &Path,
path: &Url,
position: Position,
diagnostics: Vec<Diagnostic>,
cb: impl FnOnce(PluginId, Result<CodeActionResponse, RpcError>)
@@ -621,10 +629,9 @@ impl PluginCatalogRpcHandler {
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = CodeActionRequest::METHOD;
let params = CodeActionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
range: Range {
start: position,
end: position,
@@ -637,47 +644,52 @@ impl PluginCatalogRpcHandler {
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_inlay_hints(
&self,
path: &Path,
path: &Url,
range: Range,
cb: impl FnOnce(PluginId, Result<Vec<InlayHint>, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = InlayHintRequest::METHOD;
let params = InlayHintParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
work_done_progress_params: WorkDoneProgressParams::default(),
range,
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_inline_completions(
&self,
path: &Path,
path: &Url,
position: Position,
trigger_kind: InlineCompletionTriggerKind,
cb: impl FnOnce(PluginId, Result<InlineCompletionResponse, RpcError>)
@@ -685,11 +697,10 @@ impl PluginCatalogRpcHandler {
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = InlineCompletionRequest::METHOD;
let params = InlineCompletionParams {
text_document_position: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
context: lsp_types::InlineCompletionContext {
@@ -698,39 +709,44 @@ impl PluginCatalogRpcHandler {
},
work_done_progress_params: WorkDoneProgressParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_document_symbols(
&self,
path: &Path,
path: &Url,
cb: impl FnOnce(PluginId, Result<DocumentSymbolResponse, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = DocumentSymbolRequest::METHOD;
let params = DocumentSymbolParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
@@ -754,16 +770,15 @@ impl PluginCatalogRpcHandler {
pub fn get_document_formatting(
&self,
path: &Path,
path: &Url,
cb: impl FnOnce(PluginId, Result<Vec<TextEdit>, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = Formatting::METHOD;
let params = DocumentFormattingParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
options: FormattingOptions {
tab_size: 4,
insert_spaces: true,
@@ -771,46 +786,51 @@ impl PluginCatalogRpcHandler {
},
work_done_progress_params: WorkDoneProgressParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn prepare_rename(
&self,
path: &Path,
path: &Url,
position: Position,
cb: impl FnOnce(PluginId, Result<PrepareRenameResponse, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = PrepareRenameRequest::METHOD;
let params = TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn rename(
&self,
path: &Path,
path: &Url,
position: Position,
new_name: String,
cb: impl FnOnce(PluginId, Result<WorkspaceEdit, RpcError>)
@@ -818,104 +838,112 @@ impl PluginCatalogRpcHandler {
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = Rename::METHOD;
let params = RenameParams {
text_document_position: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
new_name,
work_done_progress_params: WorkDoneProgressParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_semantic_tokens(
&self,
path: &Path,
path: &Url,
cb: impl FnOnce(PluginId, Result<SemanticTokens, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = SemanticTokensFullRequest::METHOD;
let params = SemanticTokensParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn get_selection_range(
&self,
path: &Path,
path: &Url,
positions: Vec<Position>,
cb: impl FnOnce(PluginId, Result<Vec<SelectionRange>, RpcError>)
+ Clone
+ Send
+ 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = SelectionRangeRequest::METHOD;
let params = SelectionRangeParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
positions,
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: Default::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
pub fn hover(
&self,
path: &Path,
path: &Url,
position: Position,
cb: impl FnOnce(PluginId, Result<Hover, RpcError>) + Clone + Send + 'static,
) {
let uri = Url::from_file_path(path).unwrap();
let method = HoverRequest::METHOD;
let params = HoverParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
work_done_progress_params: WorkDoneProgressParams::default(),
};
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
cb,
);
}
@@ -923,15 +951,14 @@ impl PluginCatalogRpcHandler {
pub fn completion(
&self,
request_id: usize,
path: &Path,
path: &Url,
input: String,
position: Position,
) {
let uri = Url::from_file_path(path).unwrap();
let method = Completion::METHOD;
let params = CompletionParams {
text_document_position: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
work_done_progress_params: WorkDoneProgressParams::default(),
@@ -940,14 +967,17 @@ impl PluginCatalogRpcHandler {
};
let core_rpc = self.core_rpc.clone();
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request_to_all_plugins(
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
move |plugin_id, result| {
if let Ok(value) = result {
if let Ok(resp) =
@@ -998,34 +1028,31 @@ impl PluginCatalogRpcHandler {
);
}
pub fn signature_help(
&self,
request_id: usize,
path: &Path,
position: Position,
) {
let uri = Url::from_file_path(path).unwrap();
pub fn signature_help(&self, request_id: usize, path: &Url, position: Position) {
let method = SignatureHelpRequest::METHOD;
let params = SignatureHelpParams {
// TODO: We could provide more information about the signature for the LSP to work with
context: None,
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri },
text_document: TextDocumentIdentifier { uri: path.clone() },
position,
},
work_done_progress_params: WorkDoneProgressParams::default(),
};
let core_rpc = self.core_rpc.clone();
let language_id =
Some(language_id_from_path(path).unwrap_or("").to_string());
let language_id = Some(
language_id_from_path(&path.to_file_path().unwrap())
.unwrap_or("")
.to_string(),
);
self.send_request(
None,
None,
method,
params,
language_id,
Some(path.to_path_buf()),
Some(path.clone()),
true,
move |plugin_id, result| {
if let Ok(value) = result {
@@ -1174,7 +1201,7 @@ impl PluginCatalogRpcHandler {
pub fn dap_start(
&self,
config: RunDebugConfig,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: BreakpointsMap,
) -> Result<()> {
self.catalog_notification(PluginCatalogNotification::DapStart {
config,
@@ -1243,7 +1270,7 @@ impl PluginCatalogRpcHandler {
pub fn dap_restart(
&self,
dap_id: DapId,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: BreakpointsMap,
) -> Result<()> {
self.catalog_notification(PluginCatalogNotification::DapRestart {
dap_id,
@@ -1254,8 +1281,8 @@ impl PluginCatalogRpcHandler {
pub fn dap_set_breakpoints(
&self,
dap_id: DapId,
path: PathBuf,
breakpoints: Vec<SourceBreakpoint>,
path: Url,
breakpoints: Breakpoints,
) -> Result<()> {
self.catalog_notification(PluginCatalogNotification::DapSetBreakpoints {
dap_id,
@@ -1385,7 +1412,7 @@ pub fn download_volt(volt: &VoltInfo) -> Result<VoltMetadata> {
pub fn install_volt(
catalog_rpc: PluginCatalogRpcHandler,
workspace: Option<PathBuf>,
workspace: Option<Url>,
configurations: Option<HashMap<String, serde_json::Value>>,
volt: VoltInfo,
) -> Result<()> {
+31 -29
View File
@@ -1,7 +1,6 @@
use std::{
borrow::Cow,
collections::HashMap,
path::{Path, PathBuf},
sync::{
atomic::{AtomicU64, Ordering},
Arc,
@@ -56,6 +55,7 @@ use psp_types::{
};
use serde::Serialize;
use serde_json::Value;
use url::Url;
use super::{
lsp::{DocumentFilter, LspClient},
@@ -116,14 +116,14 @@ pub enum PluginServerRpc {
method: Cow<'static, str>,
params: Params,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
rh: ResponseHandler<Value, RpcError>,
},
ServerNotification {
method: Cow<'static, str>,
params: Params,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
},
HostRequest {
id: Id,
@@ -137,7 +137,7 @@ pub enum PluginServerRpc {
},
DidSaveTextDocument {
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: Rope,
},
@@ -206,7 +206,7 @@ pub trait PluginServerHandler {
fn document_supported(
&mut self,
language_id: Option<&str>,
path: Option<&Path>,
path: Option<&Url>,
) -> bool;
fn method_registered(&mut self, method: &str) -> bool;
fn handle_host_notification(&mut self, method: String, params: Params);
@@ -224,7 +224,7 @@ pub trait PluginServerHandler {
fn handle_did_save_text_document(
&self,
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: Rope,
);
@@ -315,7 +315,7 @@ impl PluginServerRpcHandler {
method: impl Into<Cow<'static, str>>,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
) {
let params = Params::from(serde_json::to_value(params).unwrap());
@@ -344,7 +344,7 @@ impl PluginServerRpcHandler {
method: impl Into<Cow<'static, str>>,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
) -> Result<Value, RpcError> {
let (tx, rx) = crossbeam_channel::bounded(1);
@@ -369,7 +369,7 @@ impl PluginServerRpcHandler {
method: impl Into<Cow<'static, str>>,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
f: impl RpcCallback<Value, RpcError> + 'static,
) {
@@ -388,7 +388,7 @@ impl PluginServerRpcHandler {
method: Cow<'static, str>,
params: P,
language_id: Option<String>,
path: Option<PathBuf>,
path: Option<Url>,
check: bool,
rh: ResponseHandler<Value, RpcError>,
) {
@@ -438,7 +438,7 @@ impl PluginServerRpcHandler {
rh,
} => {
if handler
.document_supported(language_id.as_deref(), path.as_deref())
.document_supported(language_id.as_deref(), path.as_ref())
&& handler.method_registered(&method)
{
self.send_server_request(id, &method, params, rh);
@@ -456,7 +456,7 @@ impl PluginServerRpcHandler {
path,
} => {
if handler
.document_supported(language_id.as_deref(), path.as_deref())
.document_supported(language_id.as_deref(), path.as_ref())
&& handler.method_registered(&method)
{
self.send_server_notification(&method, params);
@@ -590,8 +590,8 @@ struct ServerRegistrations {
pub struct PluginHostHandler {
volt_id: VoltID,
volt_display_name: String,
pwd: Option<PathBuf>,
pub(crate) workspace: Option<PathBuf>,
pwd: Option<Url>,
pub(crate) workspace: Option<Url>,
document_selector: Vec<DocumentFilter>,
core_rpc: CoreRpcHandler,
catalog_rpc: PluginCatalogRpcHandler,
@@ -607,8 +607,8 @@ pub struct PluginHostHandler {
impl PluginHostHandler {
#[allow(clippy::too_many_arguments)]
pub fn new(
workspace: Option<PathBuf>,
pwd: Option<PathBuf>,
workspace: Option<Url>,
pwd: Option<Url>,
volt_id: VoltID,
volt_display_name: String,
document_selector: DocumentSelector,
@@ -638,7 +638,7 @@ impl PluginHostHandler {
pub fn document_supported(
&self,
language_id: Option<&str>,
path: Option<&Path>,
path: Option<&Url>,
) -> bool {
match language_id {
Some(language_id) => {
@@ -647,11 +647,9 @@ impl PluginHostHandler {
|| filter.language_id.as_deref() == Some(language_id))
&& (path.is_none()
|| filter.pattern.is_none()
|| filter
.pattern
.as_ref()
.unwrap()
.is_match(path.as_ref().unwrap()))
|| filter.pattern.as_ref().unwrap().is_match(
path.and_then(|p| p.to_file_path().ok()).unwrap(),
))
{
return true;
}
@@ -784,7 +782,7 @@ impl PluginHostHandler {
}
}
fn check_save_capability(&self, language_id: &str, path: &Path) -> (bool, bool) {
fn check_save_capability(&self, language_id: &str, path: &Url) -> (bool, bool) {
if self.document_supported(Some(language_id), Some(path)) {
let (should_send, include_text) = self
.server_capabilities
@@ -812,7 +810,11 @@ impl PluginHostHandler {
if (filter.language_id.is_none()
|| filter.language_id.as_deref() == Some(language_id))
&& (filter.pattern.is_none()
|| filter.pattern.as_ref().unwrap().is_match(path))
|| filter
.pattern
.as_ref()
.unwrap()
.is_match(path.to_file_path().unwrap()))
{
return (true, options.include_text);
}
@@ -1089,7 +1091,7 @@ impl PluginHostHandler {
pub fn handle_did_save_text_document(
&self,
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: Rope,
) {
@@ -1117,7 +1119,7 @@ impl PluginHostHandler {
pub fn handle_did_change_text_document(
&mut self,
lanaguage_id: String,
language_id: String,
document: VersionedTextDocumentIdentifier,
delta: RopeDelta,
text: Rope,
@@ -1170,7 +1172,7 @@ impl PluginHostHandler {
_ => return,
};
let path = document.uri.to_file_path().ok();
let path = document.uri.clone();
let params = DidChangeTextDocumentParams {
text_document: document,
@@ -1180,8 +1182,8 @@ impl PluginHostHandler {
self.server_rpc.server_notification(
DidChangeTextDocument::METHOD,
params,
Some(lanaguage_id),
path,
Some(language_id),
Some(path),
false,
);
}
+15 -11
View File
@@ -5,7 +5,7 @@ use std::{
collections::{HashMap, VecDeque},
fs,
io::{Read, Seek, Write},
path::{Path, PathBuf},
path::Path,
process,
sync::{Arc, RwLock},
thread,
@@ -23,12 +23,13 @@ use lapce_xi_rope::{Rope, RopeDelta};
use lsp_types::{
notification::Initialized, request::Initialize, DocumentFilter,
InitializeParams, InitializedParams, TextDocumentContentChangeEvent,
TextDocumentIdentifier, Url, VersionedTextDocumentIdentifier,
WorkDoneProgressParams, WorkspaceFolder,
TextDocumentIdentifier, VersionedTextDocumentIdentifier, WorkDoneProgressParams,
WorkspaceFolder,
};
use parking_lot::Mutex;
use psp_types::{Notification, Request};
use serde_json::Value;
use url::Url;
use wasi_experimental_http_wasmtime::{HttpCtx, HttpState};
use wasmtime_wasi::WasiCtxBuilder;
@@ -97,7 +98,7 @@ impl PluginServerHandler for Plugin {
fn document_supported(
&mut self,
language_id: Option<&str>,
path: Option<&Path>,
path: Option<&Url>,
) -> bool {
self.host.document_supported(language_id, path)
}
@@ -140,7 +141,7 @@ impl PluginServerHandler for Plugin {
fn handle_did_save_text_document(
&self,
language_id: String,
path: PathBuf,
path: Url,
text_document: TextDocumentIdentifier,
text: Rope,
) {
@@ -190,7 +191,7 @@ impl Plugin {
fn initialize(&mut self) {
let workspace = self.host.workspace.clone();
let configurations = self.configurations.as_ref().map(unflatten_map);
let root_uri = workspace.map(|p| Url::from_directory_path(p).unwrap());
let root_uri = workspace.clone();
let server_rpc = self.host.server_rpc.clone();
self.host.server_rpc.server_request_async(
Initialize::METHOD,
@@ -239,7 +240,7 @@ impl Plugin {
pub fn load_all_volts(
plugin_rpc: PluginCatalogRpcHandler,
extra_plugin_paths: &[PathBuf],
extra_plugin_paths: &[Url],
disabled_volts: Vec<VoltID>,
) {
let all_volts = find_all_volts(extra_plugin_paths);
@@ -263,7 +264,7 @@ pub fn load_all_volts(
/// purposes.
/// As well, this function skips any volt in the typical plugin directory that match the name
/// of the dev plugin so as to support developing a plugin you actively use.
pub fn find_all_volts(extra_plugin_paths: &[PathBuf]) -> Vec<VoltMetadata> {
pub fn find_all_volts(extra_plugin_paths: &[Url]) -> Vec<VoltMetadata> {
let Some(plugin_dir) = Directory::plugins_directory() else {
return Vec::new();
};
@@ -294,7 +295,8 @@ pub fn find_all_volts(extra_plugin_paths: &[PathBuf]) -> Vec<VoltMetadata> {
.collect();
for plugin_path in extra_plugin_paths {
let mut metadata = match load_volt(plugin_path) {
let plugin_path = plugin_path.to_file_path().unwrap();
let mut metadata = match load_volt(&plugin_path) {
Ok(metadata) => metadata,
Err(e) => {
tracing::error!("Failed to load extra plugin: {:?}", e);
@@ -405,7 +407,7 @@ pub fn enable_volt(
}
pub fn start_volt(
workspace: Option<PathBuf>,
workspace: Option<Url>,
configurations: Option<HashMap<String, serde_json::Value>>,
plugin_rpc: PluginCatalogRpcHandler,
meta: VoltMetadata,
@@ -542,7 +544,9 @@ pub fn start_volt(
id,
host: PluginHostHandler::new(
workspace,
meta.dir.clone(),
meta.dir
.clone()
.and_then(|d| Url::from_directory_path(d).ok()),
meta.id(),
meta.display_name.clone(),
meta.activation
+314
View File
@@ -0,0 +1,314 @@
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use anyhow::{anyhow, Context, Result};
use git2::{build::CheckoutBuilder, Delta, DiffDelta, DiffOptions, ErrorCode, Oid};
use lapce_rpc::source_control::{DiffInfo, FileDiff};
use url::Url;
#[derive(Clone, Debug)]
pub struct DiffHunk {
pub old_start: u32,
pub old_lines: u32,
pub new_start: u32,
pub new_lines: u32,
pub header: String,
}
pub struct Repository {
repo: git2::Repository,
}
impl Repository {
pub fn init(path: &Path) -> Result<Self> {
let repo = git2::Repository::init(path)?;
Ok(Repository { repo })
}
pub fn discover(path: &Path) -> Result<Self> {
let repo = git2::Repository::discover(path)?;
Ok(Repository { repo })
}
pub fn discover_or_init(path: &Path) -> Result<Self> {
Ok(Self::discover(path).unwrap_or(Self::init(path)?))
}
pub fn commit(&self, message: &str, diffs: Vec<FileDiff>) -> Result<()> {
let mut index = self.repo.index()?;
for diff in diffs {
match diff {
FileDiff::Modified(p) | FileDiff::Added(p) => {
index.add_path(p.strip_prefix(self.repo.path())?)?;
}
FileDiff::Renamed(a, d) => {
index.add_path(a.strip_prefix(self.repo.path())?)?;
index.remove_path(d.strip_prefix(self.repo.path())?)?;
}
FileDiff::Deleted(p) => {
index.remove_path(p.strip_prefix(self.repo.path())?)?;
}
}
}
index.write()?;
let tree = index.write_tree()?;
let tree = self.repo.find_tree(tree)?;
match self.repo.signature() {
Ok(signature) => {
let parents = self.repo
.head()
.and_then(|head| Ok(vec![head.peel_to_commit()?]))
.unwrap_or(vec![]);
let parents_refs = parents.iter().collect::<Vec<_>>();
self.repo.commit(
Some("HEAD"),
&signature,
&signature,
message,
&tree,
&parents_refs,
)?;
Ok(())
}
Err(e) => match e.code() {
ErrorCode::NotFound => Err(anyhow!(
"No user.name and/or user.email configured for this git repository."
)),
_ => Err(anyhow!(
"Error while creating commit's signature: {}",
e.message()
)),
},
}
}
pub fn checkout(&self, reference: &str) -> Result<()> {
let (object, reference) = self.repo.revparse_ext(reference)?;
self.repo.checkout_tree(&object, None)?;
self.repo.set_head(reference.unwrap().name().unwrap())?;
Ok(())
}
pub fn discard_files_changes<'a>(
&self,
files: impl Iterator<Item = &'a Path>,
) -> Result<()> {
let mut checkout_b = CheckoutBuilder::new();
checkout_b.update_only(false).force();
let mut had_path = false;
for path in files {
// Remove the workspace path so it is relative to the folder
if let Ok(path) = path.strip_prefix(self.repo.path()) {
had_path = true;
checkout_b.path(path);
}
}
if !had_path {
// If there we no paths then we do nothing
// because the default behavior of checkout builder is to select all files
// if it is not given a path
return Ok(());
}
self.repo.checkout_index(None, Some(&mut checkout_b))?;
Ok(())
}
pub fn discard_workspace_changes(&self) -> Result<()> {
let mut checkout_b = CheckoutBuilder::new();
checkout_b.force();
self.repo.checkout_index(None, Some(&mut checkout_b))?;
Ok(())
}
pub fn delta_format(&self, delta: &DiffDelta) -> Option<(Delta, Oid, PathBuf)> {
match delta.status() {
Delta::Added | Delta::Untracked => Some((
Delta::Added,
delta.new_file().id(),
delta.new_file().path().map(|p| self.repo.path().join(p))?,
)),
Delta::Deleted => Some((
Delta::Deleted,
delta.old_file().id(),
delta.old_file().path().map(|p| self.repo.path().join(p))?,
)),
Delta::Modified => Some((
Delta::Modified,
delta.new_file().id(),
delta.new_file().path().map(|p| self.repo.path().join(p))?,
)),
_ => None,
}
}
pub fn diff_new(&self) -> Option<DiffInfo> {
let name = match self.repo.head() {
Ok(head) => head.shorthand()?.to_string(),
_ => "(No branch)".to_owned(),
};
let mut branches = Vec::new();
for branch in self.repo.branches(None).ok()? {
branches.push(branch.ok()?.0.name().ok()??.to_string());
}
let mut tags = Vec::new();
if let Ok(git_tags) = self.repo.tag_names(None) {
for tag in git_tags.into_iter().flatten() {
tags.push(tag.to_owned());
}
}
let mut deltas = Vec::new();
let mut diff_options = DiffOptions::new();
let diff = self
.repo
.diff_index_to_workdir(
None,
Some(
diff_options
.include_untracked(true)
.recurse_untracked_dirs(true),
),
)
.ok()?;
for delta in diff.deltas() {
if let Some(delta) = self.delta_format(&delta) {
deltas.push(delta);
}
}
let oid = match self.repo.revparse_single("HEAD^{tree}") {
Ok(obj) => obj.id(),
_ => Oid::zero(),
};
let cached_diff = self
.repo
.diff_tree_to_index(self.repo.find_tree(oid).ok().as_ref(), None, None)
.ok();
if let Some(cached_diff) = cached_diff {
for delta in cached_diff.deltas() {
if let Some(delta) = self.delta_format(&delta) {
deltas.push(delta);
}
}
}
let mut renames = Vec::new();
let mut renamed_deltas = HashSet::new();
for (added_index, delta) in deltas.iter().enumerate() {
if delta.0 == git2::Delta::Added {
for (deleted_index, d) in deltas.iter().enumerate() {
if d.0 == git2::Delta::Deleted && d.1 == delta.1 {
renames.push((added_index, deleted_index));
renamed_deltas.insert(added_index);
renamed_deltas.insert(deleted_index);
break;
}
}
}
}
let mut file_diffs = Vec::new();
for (added_index, deleted_index) in renames.iter() {
file_diffs.push(FileDiff::Renamed(
deltas[*added_index].2.clone(),
deltas[*deleted_index].2.clone(),
));
}
for (i, delta) in deltas.iter().enumerate() {
if renamed_deltas.contains(&i) {
continue;
}
let diff = match delta.0 {
git2::Delta::Added => FileDiff::Added(delta.2.clone()),
git2::Delta::Deleted => FileDiff::Deleted(delta.2.clone()),
git2::Delta::Modified => FileDiff::Modified(delta.2.clone()),
_ => continue,
};
file_diffs.push(diff);
}
file_diffs.sort_by_key(|d| match d {
FileDiff::Modified(p)
| FileDiff::Added(p)
| FileDiff::Renamed(p, _)
| FileDiff::Deleted(p) => p.clone(),
});
Some(DiffInfo {
head: name,
branches,
tags,
diffs: file_diffs,
})
}
pub fn file_get_head(&self, path: &Path) -> Result<(String, String)> {
let head = self.repo.head()?;
let tree = head.peel_to_tree()?;
let tree_entry = tree.get_path(path.strip_prefix(self.repo.path())?)?;
let blob = self.repo.find_blob(tree_entry.id())?;
let id = blob.id().to_string();
let content = std::str::from_utf8(blob.content())
.with_context(|| "content bytes to string")?
.to_string();
Ok((id, content))
}
pub fn get_remote_file_url(&self, file: &Path) -> Result<String> {
let head = self.repo.head()?;
let target_remote = self.repo.find_remote(
self.repo
.branch_upstream_remote(head.name().unwrap())?
.as_str()
.unwrap(),
)?;
// Grab URL part of remote
let remote = target_remote
.url()
.ok_or(anyhow!("Failed to convert remote to str"))?;
let remote_url = match Url::parse(remote) {
Ok(url) => url,
Err(_) => {
// Parse URL as ssh
Url::parse(&format!("ssh://{}", remote.replacen(':', "/", 1)))?
}
};
// Get host part
let host = remote_url
.host_str()
.ok_or(anyhow!("Couldn't find remote host"))?;
// Get namespace (e.g. organisation/project in case of GitHub, org/team/team/team/../project on GitLab)
let namespace =
if let Some(stripped) = remote_url.path().strip_suffix(".git") {
stripped
} else {
remote_url.path()
};
let commit = head.peel_to_commit()?.id();
let file_path = file
.strip_prefix(self.repo.path())?
.to_str()
.ok_or(anyhow!("Couldn't convert file path to str"))?;
let url = format!("https://{host}{namespace}/blob/{commit}/{file_path}",);
Ok(url)
}
}
+1
View File
@@ -17,6 +17,7 @@ serde_json = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }
dap-types = { workspace = true }
lsp-types = { workspace = true }
lapce-xi-rope = { workspace = true }
+11 -8
View File
@@ -8,17 +8,20 @@ use std::{
};
use crossbeam_channel::{Receiver, Sender};
use dap_types::{
events::StoppedEventBody,
types::{Breakpoint, Scope, StackFrame, Variable},
};
use lsp_types::{
CompletionResponse, LogMessageParams, ProgressParams, PublishDiagnosticsParams,
ShowMessageParams, SignatureHelp,
};
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{
dap_types::{
self, DapId, RunDebugConfig, Scope, StackFrame, Stopped, ThreadId, Variable,
},
dap::{DapId, RunDebugConfig, ThreadId},
file::PathObject,
plugin::{PluginId, VoltInfo, VoltMetadata},
proxy::ProxyStatus,
@@ -41,7 +44,7 @@ pub enum CoreNotification {
status: ProxyStatus,
},
OpenFileChanged {
path: PathBuf,
path: Url,
content: String,
},
CompletionResponse {
@@ -120,7 +123,7 @@ pub enum CoreNotification {
},
DapStopped {
dap_id: DapId,
stopped: Stopped,
stopped: StoppedEventBody,
stack_frames: HashMap<ThreadId, Vec<StackFrame>>,
variables: Vec<(Scope, Vec<Variable>)>,
},
@@ -130,7 +133,7 @@ pub enum CoreNotification {
DapBreakpointsResp {
dap_id: DapId,
path: PathBuf,
breakpoints: Vec<dap_types::Breakpoint>,
breakpoints: Vec<Breakpoint>,
},
}
@@ -235,7 +238,7 @@ impl CoreRpcHandler {
self.notification(CoreNotification::DiffInfo { diff });
}
pub fn open_file_changed(&self, path: PathBuf, content: String) {
pub fn open_file_changed(&self, path: Url, content: String) {
self.notification(CoreNotification::OpenFileChanged { path, content });
}
@@ -356,7 +359,7 @@ impl CoreRpcHandler {
&self,
dap_id: DapId,
path: PathBuf,
breakpoints: Vec<dap_types::Breakpoint>,
breakpoints: Vec<Breakpoint>,
) {
self.notification(CoreNotification::DapBreakpointsResp {
dap_id,
+59
View File
@@ -0,0 +1,59 @@
use std::collections::HashMap;
use dap_types::types::Breakpoint;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::counter::Counter;
pub type Breakpoints = Vec<Breakpoint>;
pub type BreakpointsMapping = HashMap<Url, Vec<Breakpoint>>;
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ThreadId(pub u64);
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
pub struct DapId(pub u64);
impl Default for DapId {
fn default() -> Self {
Self::next()
}
}
impl DapId {
pub fn next() -> Self {
static DAP_ID_COUNTER: Counter = Counter::new();
Self(DAP_ID_COUNTER.next())
}
}
pub struct DapServer {
pub program: String,
pub arguments: Vec<String>,
pub working_directory: Option<Url>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct RunDebugProgram {
pub program: String,
pub arguments: Option<Vec<String>>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct RunDebugConfig {
#[serde(rename = "type")]
pub ty: Option<String>,
pub name: String,
pub program: String,
pub arguments: Option<Vec<String>>,
pub working_directory: Option<String>,
pub environment: Option<HashMap<String, String>>,
pub prelaunch: Option<RunDebugProgram>,
#[serde(skip)]
pub debug_command: Option<Vec<String>>,
#[serde(skip)]
pub dap_id: DapId,
}
-857
View File
@@ -1,857 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* Much of the code in this file is modified from [helix](https://github.com/helix-editor/helix)'s implementation of their syntax highlighting, which is under the MPL.
*/
use std::{collections::HashMap, path::PathBuf};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value;
use crate::counter::Counter;
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
pub struct DapId(pub u64);
impl Default for DapId {
fn default() -> Self {
Self::next()
}
}
impl DapId {
pub fn next() -> Self {
static DAP_ID_COUNTER: Counter = Counter::new();
Self(DAP_ID_COUNTER.next())
}
}
pub struct DapServer {
pub program: String,
pub args: Vec<String>,
pub cwd: Option<PathBuf>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct RunDebugProgram {
pub program: String,
pub args: Option<Vec<String>>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct RunDebugConfig {
#[serde(rename = "type")]
pub ty: Option<String>,
pub name: String,
pub program: String,
pub args: Option<Vec<String>>,
pub cwd: Option<String>,
pub env: Option<HashMap<String, String>>,
pub prelaunch: Option<RunDebugProgram>,
#[serde(skip)]
pub debug_command: Option<Vec<String>>,
#[serde(skip)]
pub dap_id: DapId,
}
pub trait Request {
type Arguments: DeserializeOwned + Serialize;
type Result: DeserializeOwned + Serialize;
const COMMAND: &'static str;
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DapRequest {
pub seq: u64,
pub command: String,
pub arguments: Option<Value>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub struct DapResponse {
pub request_seq: u64,
pub success: bool,
pub command: String,
pub message: Option<String>,
pub body: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "event", content = "body")]
// seq is omitted as unused and is not sent by some implementations
pub enum DapEvent {
Initialized(Option<DebuggerCapabilities>),
Stopped(Stopped),
Continued(Continued),
Exited(Exited),
Terminated(Option<Terminated>),
Thread {
reason: String,
thread_id: ThreadId,
},
Output(Output),
Breakpoint {
reason: String,
breakpoint: Breakpoint,
},
Module {
reason: String,
module: Module,
},
LoadedSource {
reason: String,
source: Source,
},
Process(Process),
Capabilities(Capabilities),
Memory(Memory),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum DapPayload {
Request(DapRequest),
Response(DapResponse),
Event(DapEvent),
}
#[derive(Debug)]
pub enum Initialize {}
impl Request for Initialize {
type Arguments = InitializeParams;
type Result = DebuggerCapabilities;
const COMMAND: &'static str = "initialize";
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams {
#[serde(rename = "clientID", skip_serializing_if = "Option::is_none")]
pub client_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_name: Option<String>,
#[serde(rename = "adapterID")]
pub adapter_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<String>,
#[serde(rename = "linesStartAt1", skip_serializing_if = "Option::is_none")]
pub lines_start_at_one: Option<bool>,
#[serde(rename = "columnsStartAt1", skip_serializing_if = "Option::is_none")]
pub columns_start_at_one: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path_format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_variable_type: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_variable_paging: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_run_in_terminal_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_memory_references: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_progress_reporting: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_invalidated_event: Option<bool>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ColumnDescriptor {
pub attribute_name: String,
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub ty: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<usize>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExceptionBreakpointsFilter {
pub filter: String,
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_condition: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition_description: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DebuggerCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_configuration_done_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_function_breakpoints: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_conditional_breakpoints: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_hit_conditional_breakpoints: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_evaluate_for_hovers: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_step_back: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_set_variable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_restart_frame: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_goto_targets_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_step_in_targets_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_completions_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_modules_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_restart_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_exception_options: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_value_formatting_options: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_exception_info_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub support_terminate_debuggee: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub support_suspend_debuggee: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_delayed_stack_trace_loading: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_loaded_sources_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_log_points: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_terminate_threads_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_set_expression: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_terminate_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_data_breakpoints: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_read_memory_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_write_memory_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_disassemble_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_cancel_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_breakpoint_locations_request: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_clipboard_context: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_stepping_granularity: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_instruction_breakpoints: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_exception_filter_options: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exception_breakpoint_filters: Option<Vec<ExceptionBreakpointsFilter>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_trigger_characters: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_module_columns: Option<Vec<ColumnDescriptor>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supported_checksum_algorithms: Option<Vec<String>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Stopped {
pub reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thread_id: Option<ThreadId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preserve_focus_hint: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub all_threads_stopped: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hit_breakpoint_ids: Option<Vec<usize>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Continued {
pub thread_id: ThreadId,
#[serde(skip_serializing_if = "Option::is_none")]
pub all_threads_continued: Option<bool>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Exited {
pub exit_code: usize,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Terminated {
#[serde(skip_serializing_if = "Option::is_none")]
pub restart: Option<Value>,
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Deserialize,
Serialize,
)]
pub struct ThreadId(isize);
impl std::fmt::Display for ThreadId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Thread {
pub id: ThreadId,
pub name: String,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Checksum {
pub algorithm: String,
pub checksum: String,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Source {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_reference: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presentation_hint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub origin: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sources: Option<Vec<Source>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub adapter_data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub checksums: Option<Vec<Checksum>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Output {
pub output: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variables_reference: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<Source>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SourceBreakpoint {
pub line: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hit_condition: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub log_message: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Breakpoint {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<usize>,
pub verified: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<Source>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_column: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub instruction_reference: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<usize>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Module {
pub id: String, // TODO: || number
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_optimized: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_user_code: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol_status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol_file_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date_time_stamp: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address_range: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Process {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_process_id: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_local_process: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_method: Option<String>, // TODO: use enum
#[serde(skip_serializing_if = "Option::is_none")]
pub pointer_size: Option<usize>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Capabilities {
pub capabilities: DebuggerCapabilities,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Memory {
pub memory_reference: String,
pub offset: usize,
pub count: usize,
}
pub enum Launch {}
impl Request for Launch {
type Arguments = Value;
type Result = Value;
const COMMAND: &'static str = "launch";
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RunInTerminalResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub process_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shell_process_id: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RunInTerminalArguments {
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,
pub args: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<HashMap<String, Option<String>>>,
}
#[derive(Debug)]
pub enum RunInTerminal {}
impl Request for RunInTerminal {
type Arguments = RunInTerminalArguments;
type Result = RunInTerminalResponse;
const COMMAND: &'static str = "runInTerminal";
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetBreakpointsArguments {
pub source: Source,
#[serde(skip_serializing_if = "Option::is_none")]
pub breakpoints: Option<Vec<SourceBreakpoint>>,
// lines is deprecated
#[serde(skip_serializing_if = "Option::is_none")]
pub source_modified: Option<bool>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetBreakpointsResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub breakpoints: Option<Vec<Breakpoint>>,
}
#[derive(Debug)]
pub enum SetBreakpoints {}
impl Request for SetBreakpoints {
type Arguments = SetBreakpointsArguments;
type Result = SetBreakpointsResponse;
const COMMAND: &'static str = "setBreakpoints";
}
#[derive(Debug)]
pub enum ConfigurationDone {}
impl Request for ConfigurationDone {
type Arguments = ();
type Result = ();
const COMMAND: &'static str = "configurationDone";
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ContinueArguments {
pub thread_id: ThreadId,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ContinueResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub all_threads_continued: Option<bool>,
}
#[derive(Debug)]
pub enum Continue {}
impl Request for Continue {
type Arguments = ContinueArguments;
type Result = ContinueResponse;
const COMMAND: &'static str = "continue";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreadsResponse {
pub threads: Vec<Thread>,
}
#[derive(Debug)]
pub enum Threads {}
impl Request for Threads {
type Arguments = ();
type Result = ThreadsResponse;
const COMMAND: &'static str = "threads";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackFrame {
pub id: usize,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<Source>,
pub line: usize,
pub column: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_column: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_restart: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub instruction_pointer_reference: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub module_id: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presentation_hint: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackFrameFormat {
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameter_types: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameter_names: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameter_values: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub module: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_all: Option<bool>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackTraceArguments {
pub thread_id: ThreadId,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_frame: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub levels: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<StackFrameFormat>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackTraceResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub total_frames: Option<usize>,
pub stack_frames: Vec<StackFrame>,
}
#[derive(Debug)]
pub enum StackTrace {}
impl Request for StackTrace {
type Arguments = StackTraceArguments;
type Result = StackTraceResponse;
const COMMAND: &'static str = "stackTrace";
}
#[derive(Debug)]
pub enum Disconnect {}
impl Request for Disconnect {
type Arguments = ();
type Result = ();
const COMMAND: &'static str = "disconnect";
}
#[derive(Debug)]
pub enum Terminate {}
impl Request for Terminate {
type Arguments = ();
type Result = ();
const COMMAND: &'static str = "terminate";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PauseArguments {
pub thread_id: ThreadId,
}
#[derive(Debug)]
pub enum Pause {}
impl Request for Pause {
type Arguments = PauseArguments;
type Result = ();
const COMMAND: &'static str = "pause";
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Scope {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub presentation_hint: Option<String>,
pub variables_reference: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub named_variables: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub indexed_variables: Option<usize>,
pub expensive: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<Source>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_column: Option<usize>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScopesArguments {
pub frame_id: usize,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScopesResponse {
pub scopes: Vec<Scope>,
}
#[derive(Debug)]
pub enum Scopes {}
impl Request for Scopes {
type Arguments = ScopesArguments;
type Result = ScopesResponse;
const COMMAND: &'static str = "scopes";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ValueFormat {
#[serde(skip_serializing_if = "Option::is_none")]
pub hex: Option<bool>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VariablePresentationHint {
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub visibility: Option<String>,
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Variable {
pub name: String,
pub value: String,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub ty: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presentation_hint: Option<VariablePresentationHint>,
#[serde(skip_serializing_if = "Option::is_none")]
pub evaluate_name: Option<String>,
pub variables_reference: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub named_variables: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub indexed_variables: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_reference: Option<String>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VariablesArguments {
pub variables_reference: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<ValueFormat>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VariablesResponse {
pub variables: Vec<Variable>,
}
#[derive(Debug)]
pub enum Variables {}
impl Request for Variables {
type Arguments = VariablesArguments;
type Result = VariablesResponse;
const COMMAND: &'static str = "variables";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NextArguments {
pub thread_id: ThreadId,
#[serde(skip_serializing_if = "Option::is_none")]
pub granularity: Option<String>,
}
#[derive(Debug)]
pub enum Next {}
impl Request for Next {
type Arguments = NextArguments;
type Result = ();
const COMMAND: &'static str = "next";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StepInArguments {
pub thread_id: ThreadId,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_id: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub granularity: Option<String>,
}
#[derive(Debug)]
pub enum StepIn {}
impl Request for StepIn {
type Arguments = StepInArguments;
type Result = ();
const COMMAND: &'static str = "stepIn";
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StepOutArguments {
pub thread_id: ThreadId,
#[serde(skip_serializing_if = "Option::is_none")]
pub granularity: Option<String>,
}
#[derive(Debug)]
pub enum StepOut {}
impl Request for StepOut {
type Arguments = StepOutArguments;
type Result = ();
const COMMAND: &'static str = "stepOut";
}
+103 -52
View File
@@ -1,10 +1,11 @@
use std::{
cmp::{Ord, Ordering, PartialOrd},
collections::HashMap,
path::{Path, PathBuf},
collections::{HashMap, VecDeque},
path::PathBuf,
};
use serde::{Deserialize, Serialize};
use url::Url;
/// UTF8 line and column-offset
#[derive(
@@ -50,19 +51,19 @@ impl PathObject {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FileNodeViewKind {
/// An actual file/directory
Path(PathBuf),
Path(Url),
/// We are renaming the file at this path
Renaming { path: PathBuf, err: Option<String> },
Renaming { path: Url, err: Option<String> },
/// We are naming a new file/directory
Naming { err: Option<String> },
Duplicating {
/// The path that is being duplicated
source: PathBuf,
source: Url,
err: Option<String>,
},
}
impl FileNodeViewKind {
pub fn path(&self) -> Option<&Path> {
pub fn path(&self) -> Option<&Url> {
match self {
Self::Path(path) => Some(path),
Self::Renaming { path, .. } => Some(path),
@@ -124,7 +125,7 @@ impl NamingState {
pub struct Renaming {
pub state: NamingState,
/// Original file's path
pub path: PathBuf,
pub path: Url,
pub editor_needs_reset: bool,
}
@@ -134,7 +135,7 @@ pub struct NewNode {
/// If true, then we are creating a directory
pub is_dir: bool,
/// The folder that the file/directory is being created within
pub base_path: PathBuf,
pub base_path: Url,
pub editor_needs_reset: bool,
}
@@ -142,7 +143,7 @@ pub struct NewNode {
pub struct Duplicating {
pub state: NamingState,
/// Path to the item being duplicated
pub path: PathBuf,
pub path: Url,
pub editor_needs_reset: bool,
}
@@ -224,7 +225,7 @@ impl Naming {
&self,
is_dir: bool,
level: usize,
path: &Path,
path: Url,
) -> Option<FileNodeViewData> {
match self {
Naming::NewNode(n) if n.base_path == path => Some(FileNodeViewData {
@@ -237,7 +238,7 @@ impl Naming {
}),
Naming::Duplicating(d) if d.path == path => Some(FileNodeViewData {
kind: FileNodeViewKind::Duplicating {
source: d.path.to_path_buf(),
source: d.path.clone(),
err: d.state.err().map(ToString::to_string),
},
is_dir,
@@ -259,14 +260,14 @@ pub struct FileNodeViewData {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct FileNodeItem {
pub path: PathBuf,
pub path: Url,
pub is_dir: bool,
/// Whether the directory's children have been read.
/// Does nothing if not a directory.
pub read: bool,
/// Whether the directory is open in the explorer view.
pub open: bool,
pub children: HashMap<PathBuf, FileNodeItem>,
pub children: HashMap<Url, FileNodeItem>,
/// The number of child (directories) that are open themselves
/// Used for sizing of the explorer list
pub children_open_count: usize,
@@ -286,9 +287,10 @@ impl Ord for FileNodeItem {
_ => {
let [self_file_name, other_file_name] = [&self.path, &other.path]
.map(|path| {
path.file_name()
path.path_segments()
.unwrap()
.last()
.unwrap_or_default()
.to_string_lossy()
.to_lowercase()
});
human_sort::compare(&self_file_name, &other_file_name)
@@ -317,6 +319,10 @@ impl FileNodeItem {
children
}
pub fn parent(&self, url: &Url) -> Option<Url> {
parent(url)
}
/// Returns an iterator over the ancestors of `path`, starting with the first descendant of `prefix`.
///
/// # Example:
@@ -339,54 +345,70 @@ impl FileNodeItem {
/// assert_eq!(Some(Path::new("/pre/fix/foo")), iter.next());
/// assert_eq!(Some(Path::new("/pre/fix/foo/bar")), iter.next());
/// ```
fn ancestors_rev<'a>(
&self,
path: &'a Path,
) -> Option<impl Iterator<Item = &'a Path>> {
let take = if let Ok(suffix) = path.strip_prefix(&self.path) {
suffix.components().count()
} else {
return None;
};
pub fn ancestors(&self, path: &mut Url) -> Option<impl Iterator<Item = Url>> {
ancestors(path)
}
#[allow(clippy::needless_collect)] // Ancestors is not reversible
let ancestors = path.ancestors().take(take).collect::<Vec<&Path>>();
Some(ancestors.into_iter().rev())
/// Returns an iterator over the ancestors of `path`, starting with the first descendant of `prefix`.
///
/// # Example:
/// (ignored because the function is private but I promise this passes)
/// ```rust,ignore
/// # use lapce_rpc::file::FileNodeItem;
/// # use std::path::{Path, PathBuf};
/// # use std::collections::HashMap;
/// #
/// let node_item = FileNodeItem {
/// path_buf: PathBuf::from("/pre/fix"),
/// // ...
/// # is_dir: true,
/// # read: false,
/// # open: false,
/// # children: HashMap::new(),
/// # children_open_count: 0,
///};
/// let mut iter = node_item.ancestors_rev(Path::new("/pre/fix/foo/bar")).unwrap();
/// assert_eq!(Some(Path::new("/pre/fix/foo")), iter.next());
/// assert_eq!(Some(Path::new("/pre/fix/foo/bar")), iter.next());
/// ```
fn ancestors_rev(&self, path: &mut Url) -> Option<impl Iterator<Item = Url>> {
ancestors_rev(path)
}
/// Recursively get the node at `path`.
pub fn get_file_node(&self, path: &Path) -> Option<&FileNodeItem> {
pub fn get_file_node(&self, path: &mut Url) -> Option<&FileNodeItem> {
self.ancestors_rev(path)?
.try_fold(self, |node, path| node.children.get(path))
.try_fold(self, |node, path| node.children.get(&path))
}
/// Recursively get the (mutable) node at `path`.
pub fn get_file_node_mut(&mut self, path: &Path) -> Option<&mut FileNodeItem> {
pub fn get_file_node_mut(
&mut self,
path: &mut Url,
) -> Option<&mut FileNodeItem> {
self.ancestors_rev(path)?
.try_fold(self, |node, path| node.children.get_mut(path))
.try_fold(self, |node, path| node.children.get_mut(&path))
}
/// Remove a specific child from the node.
/// The path is recursive and will remove the child from parent indicated by the path.
pub fn remove_child(&mut self, path: &Path) -> Option<FileNodeItem> {
let parent = path.parent()?;
let node = self.get_file_node_mut(parent)?;
pub fn remove_child(&mut self, path: &mut Url) -> Option<FileNodeItem> {
let node = self.get_file_node_mut(path)?;
let node = node.children.remove(path)?;
for p in path.ancestors() {
self.update_node_count(p);
for mut p in self.ancestors(path)? {
self.update_node_count(&mut p);
}
Some(node)
}
/// Add a new (unread & unopened) child to the node.
pub fn add_child(&mut self, path: &Path, is_dir: bool) -> Option<()> {
let parent = path.parent()?;
let node = self.get_file_node_mut(parent)?;
pub fn add_child(&mut self, path: &mut Url, is_dir: bool) -> Option<()> {
let node = self.get_file_node_mut(path)?;
node.children.insert(
PathBuf::from(path),
path.clone(),
FileNodeItem {
path: PathBuf::from(path),
path: path.clone(),
is_dir,
read: false,
open: false,
@@ -394,8 +416,8 @@ impl FileNodeItem {
children_open_count: 0,
},
);
for p in path.ancestors() {
self.update_node_count(p);
for mut p in self.ancestors(path)? {
self.update_node_count(&mut p);
}
Some(())
@@ -405,8 +427,8 @@ impl FileNodeItem {
/// Note: this opens the node.
pub fn set_item_children(
&mut self,
path: &Path,
children: HashMap<PathBuf, FileNodeItem>,
path: &mut Url,
children: HashMap<Url, FileNodeItem>,
) {
if let Some(node) = self.get_file_node_mut(path) {
node.open = true;
@@ -414,18 +436,18 @@ impl FileNodeItem {
node.children = children;
}
for p in path.ancestors() {
self.update_node_count(p);
for mut p in self.ancestors(path).unwrap() {
self.update_node_count(&mut p);
}
}
pub fn update_node_count_recursive(&mut self, path: &Path) {
for current_path in path.ancestors() {
self.update_node_count(current_path);
pub fn update_node_count_recursive(&mut self, path: &mut Url) {
for mut current_path in self.ancestors(path).unwrap() {
self.update_node_count(&mut current_path);
}
}
pub fn update_node_count(&mut self, path: &Path) -> Option<()> {
pub fn update_node_count(&mut self, path: &mut Url) -> Option<()> {
let node = self.get_file_node_mut(path)?;
if node.is_dir {
node.children_open_count = if node.open {
@@ -490,7 +512,8 @@ impl FileNodeItem {
mut i: usize,
level: usize,
) -> usize {
let mut naming_extra = naming.extra_node(self.is_dir, level, &self.path);
let mut naming_extra =
naming.extra_node(self.is_dir, level, self.path.clone());
if !self.open {
// If the folder isn't open, then we just put it right at the top
@@ -560,3 +583,31 @@ impl FileNodeItem {
i
}
}
pub fn parent(url: &Url) -> Option<Url> {
let mut url = url.clone();
let mut segments = url.path_segments()?;
let _ = segments.next_back();
url.set_path(&segments.collect::<Vec<&str>>().join("/"));
Some(url)
}
pub fn ancestors(path: &mut Url) -> Option<impl Iterator<Item = Url>> {
let mut ancestors = vec![];
for _ in path.path_segments()?.collect::<Vec<_>>() {
let url = parent(path)?.clone();
ancestors.push(url);
}
Some(ancestors.into_iter())
}
fn ancestors_rev(path: &mut Url) -> Option<impl Iterator<Item = Url>> {
let mut ancestors = VecDeque::new();
for _ in path.path_segments()?.collect::<Vec<_>>() {
let url = parent(path)?.clone();
ancestors.push_front(url);
}
Some(ancestors.into_iter())
}
+2 -1
View File
@@ -3,7 +3,7 @@
pub mod buffer;
pub mod core;
pub mod counter;
pub mod dap_types;
pub mod dap;
pub mod file;
mod parse;
pub mod plugin;
@@ -12,6 +12,7 @@ pub mod source_control;
pub mod stdio;
pub mod style;
pub mod terminal;
pub mod url;
pub use parse::{Call, RequestId, RpcObject};
use serde::{Deserialize, Serialize};
+91 -108
View File
@@ -1,6 +1,5 @@
use std::{
collections::HashMap,
path::PathBuf,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
@@ -8,6 +7,7 @@ use std::{
};
use crossbeam_channel::{Receiver, Sender};
use dap_types::types::{Scope, SourceBreakpoint, Variable};
use indexmap::IndexMap;
use lapce_xi_rope::RopeDelta;
use lsp_types::{
@@ -19,11 +19,12 @@ use lsp_types::{
};
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use url::Url;
use super::plugin::VoltID;
use crate::{
buffer::BufferId,
dap_types::{self, DapId, RunDebugConfig, SourceBreakpoint, ThreadId},
dap::{Breakpoints, BreakpointsMapping, DapId, RunDebugConfig, ThreadId},
file::{FileNodeItem, PathObject},
plugin::{PluginId, VoltInfo, VoltMetadata},
source_control::FileDiff,
@@ -60,10 +61,10 @@ pub struct SearchMatch {
pub enum ProxyRequest {
NewBuffer {
buffer_id: BufferId,
path: PathBuf,
path: Url,
},
BufferHead {
path: PathBuf,
path: Url,
},
GlobalSearch {
pattern: String,
@@ -81,7 +82,7 @@ pub enum ProxyRequest {
},
GetHover {
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
},
GetSignature {
@@ -89,101 +90,101 @@ pub enum ProxyRequest {
position: Position,
},
GetSelectionRange {
path: PathBuf,
path: Url,
positions: Vec<Position>,
},
GitGetRemoteFileUrl {
file: PathBuf,
file: Url,
},
GetReferences {
path: PathBuf,
path: Url,
position: Position,
},
GetDefinition {
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
},
GetTypeDefinition {
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
},
GetInlayHints {
path: PathBuf,
path: Url,
},
GetInlineCompletions {
path: PathBuf,
path: Url,
position: Position,
trigger_kind: InlineCompletionTriggerKind,
},
GetSemanticTokens {
path: PathBuf,
path: Url,
},
PrepareRename {
path: PathBuf,
path: Url,
position: Position,
},
Rename {
path: PathBuf,
path: Url,
position: Position,
new_name: String,
},
GetCodeActions {
path: PathBuf,
path: Url,
position: Position,
diagnostics: Vec<Diagnostic>,
},
GetDocumentSymbols {
path: PathBuf,
path: Url,
},
GetWorkspaceSymbols {
/// The search query
query: String,
},
GetDocumentFormatting {
path: PathBuf,
path: Url,
},
GetOpenFilesContent {},
GetFiles {
path: String,
},
ReadDir {
path: PathBuf,
path: Url,
},
Save {
rev: u64,
path: PathBuf,
path: Url,
/// Whether to create the parent directories if they do not exist.
create_parents: bool,
},
SaveBufferAs {
buffer_id: BufferId,
path: PathBuf,
path: Url,
rev: u64,
content: String,
/// Whether to create the parent directories if they do not exist.
create_parents: bool,
},
CreateFile {
path: PathBuf,
path: Url,
},
CreateDirectory {
path: PathBuf,
path: Url,
},
TrashPath {
path: PathBuf,
path: Url,
},
DuplicatePath {
existing_path: PathBuf,
new_path: PathBuf,
existing_path: Url,
new_path: Url,
},
RenamePath {
from: PathBuf,
to: PathBuf,
from: Url,
to: Url,
},
TestCreateAtPath {
path: PathBuf,
path: Url,
},
DapVariable {
dap_id: DapId,
@@ -200,16 +201,16 @@ pub enum ProxyRequest {
#[serde(tag = "method", content = "params")]
pub enum ProxyNotification {
Initialize {
workspace: Option<PathBuf>,
workspace: Option<Url>,
disabled_volts: Vec<VoltID>,
/// Paths to extra plugins that should be loaded
extra_plugin_paths: Vec<PathBuf>,
extra_plugin_paths: Vec<Url>,
plugin_configurations: HashMap<String, HashMap<String, serde_json::Value>>,
window_id: usize,
tab_id: usize,
},
OpenFileChanged {
path: PathBuf,
path: Url,
},
OpenPaths {
paths: Vec<PathObject>,
@@ -217,17 +218,17 @@ pub enum ProxyNotification {
Shutdown {},
Completion {
request_id: usize,
path: PathBuf,
path: Url,
input: String,
position: Position,
},
SignatureHelp {
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
},
Update {
path: PathBuf,
path: Url,
delta: RopeDelta,
rev: u64,
},
@@ -261,7 +262,7 @@ pub enum ProxyNotification {
reference: String,
},
GitDiscardFilesChanges {
files: Vec<PathBuf>,
files: Vec<Url>,
},
GitDiscardWorkspaceChanges {},
GitInit {},
@@ -279,7 +280,7 @@ pub enum ProxyNotification {
},
DapStart {
config: RunDebugConfig,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: HashMap<Url, Vec<SourceBreakpoint>>,
},
DapProcessId {
dap_id: DapId,
@@ -314,12 +315,12 @@ pub enum ProxyNotification {
},
DapRestart {
dap_id: DapId,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: BreakpointsMapping,
},
DapSetBreakpoints {
dap_id: DapId,
path: PathBuf,
breakpoints: Vec<SourceBreakpoint>,
path: Url,
breakpoints: Breakpoints,
},
}
@@ -367,7 +368,7 @@ pub enum ProxyResponse {
resp: CodeActionResponse,
},
GetFilesResponse {
items: Vec<PathBuf>,
items: Vec<Url>,
},
GetDocumentFormatting {
edits: Vec<TextEdit>,
@@ -400,16 +401,16 @@ pub enum ProxyResponse {
items: Vec<TextDocumentItem>,
},
GlobalSearchResponse {
matches: IndexMap<PathBuf, Vec<SearchMatch>>,
matches: IndexMap<Url, Vec<SearchMatch>>,
},
DapVariableResponse {
varialbes: Vec<dap_types::Variable>,
varialbes: Vec<Variable>,
},
DapGetScopesResponse {
scopes: Vec<(dap_types::Scope, Vec<dap_types::Variable>)>,
scopes: Vec<(Scope, Vec<Variable>)>,
},
CreatePathResponse {
path: PathBuf,
path: Url,
},
Success {},
SaveResponse {},
@@ -419,7 +420,7 @@ pub type ProxyMessage = RpcMessage<ProxyRequest, ProxyNotification, ProxyRespons
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadDirResponse {
pub items: HashMap<PathBuf, FileNodeItem>,
pub items: HashMap<Url, FileNodeItem>,
}
pub trait ProxyCallback: Send + FnOnce(Result<ProxyResponse, RpcError>) {}
@@ -532,6 +533,14 @@ impl ProxyRpcHandler {
let _ = self.tx.send(ProxyRpc::Notification(notification));
}
pub fn get_os(&self) -> &'static str {
std::env::consts::OS
}
pub fn get_arch(&self) -> &'static str {
std::env::consts::ARCH
}
pub fn git_init(&self) {
self.notification(ProxyNotification::GitInit {});
}
@@ -571,9 +580,9 @@ impl ProxyRpcHandler {
pub fn initialize(
&self,
workspace: Option<PathBuf>,
workspace: Option<Url>,
disabled_volts: Vec<VoltID>,
extra_plugin_paths: Vec<PathBuf>,
extra_plugin_paths: Vec<Url>,
plugin_configurations: HashMap<String, HashMap<String, serde_json::Value>>,
window_id: usize,
tab_id: usize,
@@ -591,7 +600,7 @@ impl ProxyRpcHandler {
pub fn completion(
&self,
request_id: usize,
path: PathBuf,
path: Url,
input: String,
position: Position,
) {
@@ -603,12 +612,7 @@ impl ProxyRpcHandler {
});
}
pub fn signature_help(
&self,
request_id: usize,
path: PathBuf,
position: Position,
) {
pub fn signature_help(&self, request_id: usize, path: Url, position: Position) {
self.notification(ProxyNotification::SignatureHelp {
request_id,
path,
@@ -639,32 +643,32 @@ impl ProxyRpcHandler {
pub fn new_buffer(
&self,
buffer_id: BufferId,
path: PathBuf,
path: Url,
f: impl ProxyCallback + 'static,
) {
self.request_async(ProxyRequest::NewBuffer { buffer_id, path }, f);
}
pub fn get_buffer_head(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
pub fn get_buffer_head(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::BufferHead { path }, f);
}
pub fn create_file(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
pub fn create_file(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::CreateFile { path }, f);
}
pub fn create_directory(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
pub fn create_directory(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::CreateDirectory { path }, f);
}
pub fn trash_path(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
pub fn trash_path(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::TrashPath { path }, f);
}
pub fn duplicate_path(
&self,
existing_path: PathBuf,
new_path: PathBuf,
existing_path: Url,
new_path: Url,
f: impl ProxyCallback + 'static,
) {
self.request_async(
@@ -676,27 +680,18 @@ impl ProxyRpcHandler {
);
}
pub fn rename_path(
&self,
from: PathBuf,
to: PathBuf,
f: impl ProxyCallback + 'static,
) {
pub fn rename_path(&self, from: Url, to: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::RenamePath { from, to }, f);
}
pub fn test_create_at_path(
&self,
path: PathBuf,
f: impl ProxyCallback + 'static,
) {
pub fn test_create_at_path(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::TestCreateAtPath { path }, f);
}
pub fn save_buffer_as(
&self,
buffer_id: BufferId,
path: PathBuf,
path: Url,
rev: u64,
content: String,
create_parents: bool,
@@ -736,7 +731,7 @@ impl ProxyRpcHandler {
pub fn save(
&self,
rev: u64,
path: PathBuf,
path: Url,
create_parents: bool,
f: impl ProxyCallback + 'static,
) {
@@ -763,7 +758,7 @@ impl ProxyRpcHandler {
self.request(ProxyRequest::GetOpenFilesContent {})
}
pub fn read_dir(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
pub fn read_dir(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::ReadDir { path }, f);
}
@@ -800,7 +795,7 @@ impl ProxyRpcHandler {
pub fn get_hover(
&self,
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
f: impl ProxyCallback + 'static,
) {
@@ -817,7 +812,7 @@ impl ProxyRpcHandler {
pub fn get_definition(
&self,
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
f: impl ProxyCallback + 'static,
) {
@@ -834,7 +829,7 @@ impl ProxyRpcHandler {
pub fn get_type_definition(
&self,
request_id: usize,
path: PathBuf,
path: Url,
position: Position,
f: impl ProxyCallback + 'static,
) {
@@ -850,7 +845,7 @@ impl ProxyRpcHandler {
pub fn get_references(
&self,
path: PathBuf,
path: Url,
position: Position,
f: impl ProxyCallback + 'static,
) {
@@ -859,7 +854,7 @@ impl ProxyRpcHandler {
pub fn get_code_actions(
&self,
path: PathBuf,
path: Url,
position: Position,
diagnostics: Vec<Diagnostic>,
f: impl ProxyCallback + 'static,
@@ -876,25 +871,17 @@ impl ProxyRpcHandler {
pub fn get_document_formatting(
&self,
path: PathBuf,
path: Url,
f: impl ProxyCallback + 'static,
) {
self.request_async(ProxyRequest::GetDocumentFormatting { path }, f);
}
pub fn get_semantic_tokens(
&self,
path: PathBuf,
f: impl ProxyCallback + 'static,
) {
pub fn get_semantic_tokens(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::GetSemanticTokens { path }, f);
}
pub fn get_document_symbols(
&self,
path: PathBuf,
f: impl ProxyCallback + 'static,
) {
pub fn get_document_symbols(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::GetDocumentSymbols { path }, f);
}
@@ -908,7 +895,7 @@ impl ProxyRpcHandler {
pub fn prepare_rename(
&self,
path: PathBuf,
path: Url,
position: Position,
f: impl ProxyCallback + 'static,
) {
@@ -917,7 +904,7 @@ impl ProxyRpcHandler {
pub fn git_get_remote_file_url(
&self,
file: PathBuf,
file: Url,
f: impl ProxyCallback + 'static,
) {
self.request_async(ProxyRequest::GitGetRemoteFileUrl { file }, f);
@@ -925,7 +912,7 @@ impl ProxyRpcHandler {
pub fn rename(
&self,
path: PathBuf,
path: Url,
position: Position,
new_name: String,
f: impl ProxyCallback + 'static,
@@ -940,13 +927,13 @@ impl ProxyRpcHandler {
);
}
pub fn get_inlay_hints(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
pub fn get_inlay_hints(&self, path: Url, f: impl ProxyCallback + 'static) {
self.request_async(ProxyRequest::GetInlayHints { path }, f);
}
pub fn get_inline_completions(
&self,
path: PathBuf,
path: Url,
position: Position,
trigger_kind: InlineCompletionTriggerKind,
f: impl ProxyCallback + 'static,
@@ -961,7 +948,7 @@ impl ProxyRpcHandler {
);
}
pub fn update(&self, path: PathBuf, delta: RopeDelta, rev: u64) {
pub fn update(&self, path: Url, delta: RopeDelta, rev: u64) {
self.notification(ProxyNotification::Update { path, delta, rev });
}
@@ -972,7 +959,7 @@ impl ProxyRpcHandler {
self.notification(ProxyNotification::UpdatePluginConfigs { configs });
}
pub fn git_discard_files_changes(&self, files: Vec<PathBuf>) {
pub fn git_discard_files_changes(&self, files: Vec<Url>) {
self.notification(ProxyNotification::GitDiscardFilesChanges { files });
}
@@ -982,7 +969,7 @@ impl ProxyRpcHandler {
pub fn get_selection_range(
&self,
path: PathBuf,
path: Url,
positions: Vec<Position>,
f: impl ProxyCallback + 'static,
) {
@@ -992,7 +979,7 @@ impl ProxyRpcHandler {
pub fn dap_start(
&self,
config: RunDebugConfig,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
breakpoints: HashMap<Url, Vec<SourceBreakpoint>>,
) {
self.notification(ProxyNotification::DapStart {
config,
@@ -1013,11 +1000,7 @@ impl ProxyRpcHandler {
})
}
pub fn dap_restart(
&self,
dap_id: DapId,
breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
) {
pub fn dap_restart(&self, dap_id: DapId, breakpoints: BreakpointsMapping) {
self.notification(ProxyNotification::DapRestart {
dap_id,
breakpoints,
@@ -1055,8 +1038,8 @@ impl ProxyRpcHandler {
pub fn dap_set_breakpoints(
&self,
dap_id: DapId,
path: PathBuf,
breakpoints: Vec<SourceBreakpoint>,
path: Url,
breakpoints: Breakpoints,
) {
self.notification(ProxyNotification::DapSetBreakpoints {
dap_id,
+37
View File
@@ -0,0 +1,37 @@
// use std::iter::FusedIterator;
// #[derive(Debug, Clone)]
// #[repr(transparent)]
// pub struct Url {
// pub(crate) inner: url::Url,
// }
// impl Url {
// pub fn parent(&mut self) -> Option<&mut Self> {
// self.inner.path_segments_mut().ok()?.pop();
// Some(self)
// }
// pub fn ancestors(&self) -> Ancestors {
// Ancestors { next: Some(self) }
// }
// }
// #[derive(Debug)]
// #[must_use = "iterators are lazy and do nothing unless consumed"]
// pub struct Ancestors<'a> {
// next: Option<&'a mut Url>,
// }
// impl<'a> Iterator for Ancestors<'a> {
// type Item = &'a mut Url;
// #[inline]
// fn next(&mut self) -> std::option::Option<Self::Item> {
// let next = &self.next;
// self.next = next.and_then(self::Url::parent);
// next.as_deref()
// }
// }
// impl FusedIterator for Ancestors<'_> {}