Compare commits
1 Commits
feat/arm-ci
...
feat/url
| Author | SHA1 | Date | |
|---|---|---|---|
| 95f7c53ff9 |
+2
-2
@@ -1,4 +1,4 @@
|
||||
max_width = 85
|
||||
# below requires nightly
|
||||
# imports_granularity = "Crate"
|
||||
# group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Crate"
|
||||
group_imports = "StdExternalCrate"
|
||||
|
||||
Generated
+14
@@ -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
@@ -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
@@ -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",
|
||||
# ]
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use tracing::{event, Level};
|
||||
use url::Url;
|
||||
|
||||
|
||||
@@ -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,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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod cli;
|
||||
pub mod dispatch;
|
||||
pub mod plugin;
|
||||
pub mod terminal;
|
||||
pub mod vcs;
|
||||
pub mod watcher;
|
||||
|
||||
use std::{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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<()> {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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<'_> {}
|
||||
Reference in New Issue
Block a user