mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 11:26:42 +03:00
Unify path:row:column parsing, use it in CLI
This commit is contained in:
parent
89fe5c6b09
commit
d719352152
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1097,6 +1097,7 @@ dependencies = [
|
|||||||
"plist",
|
"plist",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
|
"util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2675,6 +2676,7 @@ dependencies = [
|
|||||||
"postage",
|
"postage",
|
||||||
"settings",
|
"settings",
|
||||||
"text",
|
"text",
|
||||||
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ dirs = "3.0"
|
|||||||
ipc-channel = "0.16"
|
ipc-channel = "0.16"
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_derive.workspace = true
|
serde_derive.workspace = true
|
||||||
|
util = { path = "../util" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-foundation = "0.9"
|
core-foundation = "0.9"
|
||||||
|
@ -16,6 +16,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
ptr,
|
ptr,
|
||||||
};
|
};
|
||||||
|
use util::paths::PathLikeWithPosition;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(name = "zed", global_setting(clap::AppSettings::NoAutoVersion))]
|
#[clap(name = "zed", global_setting(clap::AppSettings::NoAutoVersion))]
|
||||||
@ -24,8 +25,11 @@ struct Args {
|
|||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
wait: bool,
|
wait: bool,
|
||||||
/// A sequence of space-separated paths that you want to open.
|
/// A sequence of space-separated paths that you want to open.
|
||||||
#[clap()]
|
///
|
||||||
paths: Vec<PathBuf>,
|
/// Use `path:line:row` syntax to open a file at a specific location.
|
||||||
|
/// Non-existing paths and directories will ignore `:line:row` suffix.
|
||||||
|
#[clap(value_parser = parse_path_with_position)]
|
||||||
|
paths_with_position: Vec<PathLikeWithPosition<PathBuf>>,
|
||||||
/// Print Zed's version and the app path.
|
/// Print Zed's version and the app path.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
version: bool,
|
version: bool,
|
||||||
@ -34,6 +38,14 @@ struct Args {
|
|||||||
bundle_path: Option<PathBuf>,
|
bundle_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_path_with_position(
|
||||||
|
argument_str: &str,
|
||||||
|
) -> Result<PathLikeWithPosition<PathBuf>, std::convert::Infallible> {
|
||||||
|
PathLikeWithPosition::parse_str(argument_str, |path_str| {
|
||||||
|
Ok(Path::new(path_str).to_path_buf())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct InfoPlist {
|
struct InfoPlist {
|
||||||
#[serde(rename = "CFBundleShortVersionString")]
|
#[serde(rename = "CFBundleShortVersionString")]
|
||||||
@ -50,7 +62,11 @@ fn main() -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
for path in args.paths.iter() {
|
for path in args
|
||||||
|
.paths_with_position
|
||||||
|
.iter()
|
||||||
|
.map(|path_with_position| &path_with_position.path_like)
|
||||||
|
{
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
touch(path.as_path())?;
|
touch(path.as_path())?;
|
||||||
}
|
}
|
||||||
@ -60,9 +76,13 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
tx.send(CliRequest::Open {
|
tx.send(CliRequest::Open {
|
||||||
paths: args
|
paths: args
|
||||||
.paths
|
.paths_with_position
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|path| fs::canonicalize(path).map_err(|error| anyhow!(error)))
|
// TODO kb continue sendint path with the position further
|
||||||
|
.map(|path_with_position| path_with_position.path_like)
|
||||||
|
.map(|path| {
|
||||||
|
fs::canonicalize(&path).with_context(|| format!("path {path:?} canonicalization"))
|
||||||
|
})
|
||||||
.collect::<Result<Vec<PathBuf>>>()?,
|
.collect::<Result<Vec<PathBuf>>>()?,
|
||||||
wait: args.wait,
|
wait: args.wait,
|
||||||
})?;
|
})?;
|
||||||
|
@ -98,7 +98,6 @@ const MAX_SELECTION_HISTORY_LEN: usize = 1024;
|
|||||||
const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
|
const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
|
||||||
|
|
||||||
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
pub const FILE_ROW_COLUMN_DELIMITER: char = ':';
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, PartialEq, Default)]
|
#[derive(Clone, Deserialize, PartialEq, Default)]
|
||||||
pub struct SelectNext {
|
pub struct SelectNext {
|
||||||
|
@ -2,7 +2,6 @@ use crate::{
|
|||||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
||||||
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
||||||
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
||||||
FILE_ROW_COLUMN_DELIMITER,
|
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
@ -28,7 +27,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use text::Selection;
|
use text::Selection;
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
|
||||||
use workspace::item::{BreadcrumbText, FollowableItemHandle};
|
use workspace::item::{BreadcrumbText, FollowableItemHandle};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
|
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor, FILE_ROW_COLUMN_DELIMITER};
|
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
|
||||||
use fuzzy::PathMatch;
|
use fuzzy::PathMatch;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
||||||
@ -14,7 +14,7 @@ use std::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use text::Point;
|
use text::Point;
|
||||||
use util::{post_inc, ResultExt};
|
use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub type FileFinder = Picker<FileFinderDelegate>;
|
pub type FileFinder = Picker<FileFinderDelegate>;
|
||||||
@ -25,7 +25,7 @@ pub struct FileFinderDelegate {
|
|||||||
search_count: usize,
|
search_count: usize,
|
||||||
latest_search_id: usize,
|
latest_search_id: usize,
|
||||||
latest_search_did_cancel: bool,
|
latest_search_did_cancel: bool,
|
||||||
latest_search_query: Option<FileSearchQuery>,
|
latest_search_query: Option<PathLikeWithPosition<FileSearchQuery>>,
|
||||||
relative_to: Option<Arc<Path>>,
|
relative_to: Option<Arc<Path>>,
|
||||||
matches: Vec<PathMatch>,
|
matches: Vec<PathMatch>,
|
||||||
selected: Option<(usize, Arc<Path>)>,
|
selected: Option<(usize, Arc<Path>)>,
|
||||||
@ -66,31 +66,9 @@ pub enum Event {
|
|||||||
struct FileSearchQuery {
|
struct FileSearchQuery {
|
||||||
raw_query: String,
|
raw_query: String,
|
||||||
file_query_end: Option<usize>,
|
file_query_end: Option<usize>,
|
||||||
file_row: Option<u32>,
|
|
||||||
file_column: Option<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSearchQuery {
|
impl FileSearchQuery {
|
||||||
fn new(raw_query: String) -> Self {
|
|
||||||
let mut components = raw_query
|
|
||||||
.as_str()
|
|
||||||
.splitn(3, FILE_ROW_COLUMN_DELIMITER)
|
|
||||||
.map(str::trim)
|
|
||||||
.fuse();
|
|
||||||
let file_query = components.next().filter(|str| !str.is_empty());
|
|
||||||
let file_row = components.next().and_then(|row| row.parse::<u32>().ok());
|
|
||||||
let file_column = components.next().and_then(|col| col.parse::<u32>().ok());
|
|
||||||
|
|
||||||
Self {
|
|
||||||
file_query_end: file_query
|
|
||||||
.filter(|_| file_row.is_some())
|
|
||||||
.map(|query| query.len()),
|
|
||||||
file_row,
|
|
||||||
file_column,
|
|
||||||
raw_query,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path_query(&self) -> &str {
|
fn path_query(&self) -> &str {
|
||||||
match self.file_query_end {
|
match self.file_query_end {
|
||||||
Some(file_path_end) => &self.raw_query[..file_path_end],
|
Some(file_path_end) => &self.raw_query[..file_path_end],
|
||||||
@ -152,7 +130,7 @@ impl FileFinderDelegate {
|
|||||||
|
|
||||||
fn spawn_search(
|
fn spawn_search(
|
||||||
&mut self,
|
&mut self,
|
||||||
query: FileSearchQuery,
|
query: PathLikeWithPosition<FileSearchQuery>,
|
||||||
cx: &mut ViewContext<FileFinder>,
|
cx: &mut ViewContext<FileFinder>,
|
||||||
) -> Task<()> {
|
) -> Task<()> {
|
||||||
let relative_to = self.relative_to.clone();
|
let relative_to = self.relative_to.clone();
|
||||||
@ -183,7 +161,7 @@ impl FileFinderDelegate {
|
|||||||
cx.spawn(|picker, mut cx| async move {
|
cx.spawn(|picker, mut cx| async move {
|
||||||
let matches = fuzzy::match_path_sets(
|
let matches = fuzzy::match_path_sets(
|
||||||
candidate_sets.as_slice(),
|
candidate_sets.as_slice(),
|
||||||
query.path_query(),
|
query.path_like.path_query(),
|
||||||
relative_to,
|
relative_to,
|
||||||
false,
|
false,
|
||||||
100,
|
100,
|
||||||
@ -206,18 +184,18 @@ impl FileFinderDelegate {
|
|||||||
&mut self,
|
&mut self,
|
||||||
search_id: usize,
|
search_id: usize,
|
||||||
did_cancel: bool,
|
did_cancel: bool,
|
||||||
query: FileSearchQuery,
|
query: PathLikeWithPosition<FileSearchQuery>,
|
||||||
matches: Vec<PathMatch>,
|
matches: Vec<PathMatch>,
|
||||||
cx: &mut ViewContext<FileFinder>,
|
cx: &mut ViewContext<FileFinder>,
|
||||||
) {
|
) {
|
||||||
if search_id >= self.latest_search_id {
|
if search_id >= self.latest_search_id {
|
||||||
self.latest_search_id = search_id;
|
self.latest_search_id = search_id;
|
||||||
if self.latest_search_did_cancel
|
if self.latest_search_did_cancel
|
||||||
&& Some(query.path_query())
|
&& Some(query.path_like.path_query())
|
||||||
== self
|
== self
|
||||||
.latest_search_query
|
.latest_search_query
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|query| query.path_query())
|
.map(|query| query.path_like.path_query())
|
||||||
{
|
{
|
||||||
util::extend_sorted(&mut self.matches, matches.into_iter(), 100, |a, b| b.cmp(a));
|
util::extend_sorted(&mut self.matches, matches.into_iter(), 100, |a, b| b.cmp(a));
|
||||||
} else {
|
} else {
|
||||||
@ -265,7 +243,19 @@ impl PickerDelegate for FileFinderDelegate {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
Task::ready(())
|
Task::ready(())
|
||||||
} else {
|
} else {
|
||||||
self.spawn_search(FileSearchQuery::new(raw_query), cx)
|
let raw_query = &raw_query;
|
||||||
|
let query = PathLikeWithPosition::parse_str(raw_query, |path_like_str| {
|
||||||
|
Ok::<_, std::convert::Infallible>(FileSearchQuery {
|
||||||
|
raw_query: raw_query.to_owned(),
|
||||||
|
file_query_end: if path_like_str == raw_query {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(path_like_str.len())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.expect("infallible");
|
||||||
|
self.spawn_search(query, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,12 +276,12 @@ impl PickerDelegate for FileFinderDelegate {
|
|||||||
let row = self
|
let row = self
|
||||||
.latest_search_query
|
.latest_search_query
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|query| query.file_row)
|
.and_then(|query| query.row)
|
||||||
.map(|row| row.saturating_sub(1));
|
.map(|row| row.saturating_sub(1));
|
||||||
let col = self
|
let col = self
|
||||||
.latest_search_query
|
.latest_search_query
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|query| query.file_column)
|
.and_then(|query| query.column)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
.saturating_sub(1);
|
.saturating_sub(1);
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
@ -477,10 +467,13 @@ mod tests {
|
|||||||
.latest_search_query
|
.latest_search_query
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Finder should have a query after the update_matches call");
|
.expect("Finder should have a query after the update_matches call");
|
||||||
assert_eq!(latest_search_query.raw_query, query_inside_file);
|
assert_eq!(latest_search_query.path_like.raw_query, query_inside_file);
|
||||||
assert_eq!(latest_search_query.file_row, Some(file_row));
|
assert_eq!(
|
||||||
assert_eq!(latest_search_query.file_column, Some(file_column as u32));
|
latest_search_query.path_like.file_query_end,
|
||||||
assert_eq!(latest_search_query.file_query_end, Some(file_query.len()));
|
Some(file_query.len())
|
||||||
|
);
|
||||||
|
assert_eq!(latest_search_query.row, Some(file_row));
|
||||||
|
assert_eq!(latest_search_query.column, Some(file_column as u32));
|
||||||
});
|
});
|
||||||
|
|
||||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||||
@ -564,10 +557,13 @@ mod tests {
|
|||||||
.latest_search_query
|
.latest_search_query
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Finder should have a query after the update_matches call");
|
.expect("Finder should have a query after the update_matches call");
|
||||||
assert_eq!(latest_search_query.raw_query, query_outside_file);
|
assert_eq!(latest_search_query.path_like.raw_query, query_outside_file);
|
||||||
assert_eq!(latest_search_query.file_row, Some(file_row));
|
assert_eq!(
|
||||||
assert_eq!(latest_search_query.file_column, Some(file_column as u32));
|
latest_search_query.path_like.file_query_end,
|
||||||
assert_eq!(latest_search_query.file_query_end, Some(file_query.len()));
|
Some(file_query.len())
|
||||||
|
);
|
||||||
|
assert_eq!(latest_search_query.row, Some(file_row));
|
||||||
|
assert_eq!(latest_search_query.column, Some(file_column as u32));
|
||||||
});
|
});
|
||||||
|
|
||||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||||
@ -634,7 +630,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let query = FileSearchQuery::new("hi".to_string());
|
let query = test_path_like("hi");
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.delegate_mut().spawn_search(query.clone(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search(query.clone(), cx))
|
||||||
.await;
|
.await;
|
||||||
@ -719,8 +715,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| {
|
.update(cx, |f, cx| {
|
||||||
f.delegate_mut()
|
f.delegate_mut().spawn_search(test_path_like("hi"), cx)
|
||||||
.spawn_search(FileSearchQuery::new("hi".to_string()), cx)
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 7));
|
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 7));
|
||||||
@ -758,8 +753,7 @@ mod tests {
|
|||||||
// is included in the matching, because the worktree is a single file.
|
// is included in the matching, because the worktree is a single file.
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| {
|
.update(cx, |f, cx| {
|
||||||
f.delegate_mut()
|
f.delegate_mut().spawn_search(test_path_like("thf"), cx)
|
||||||
.spawn_search(FileSearchQuery::new("thf".to_string()), cx)
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
@ -779,8 +773,7 @@ mod tests {
|
|||||||
// not match anything.
|
// not match anything.
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| {
|
.update(cx, |f, cx| {
|
||||||
f.delegate_mut()
|
f.delegate_mut().spawn_search(test_path_like("thf/"), cx)
|
||||||
.spawn_search(FileSearchQuery::new("thf/".to_string()), cx)
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0));
|
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0));
|
||||||
@ -826,8 +819,7 @@ mod tests {
|
|||||||
// Run a search that matches two files with the same relative path.
|
// Run a search that matches two files with the same relative path.
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| {
|
.update(cx, |f, cx| {
|
||||||
f.delegate_mut()
|
f.delegate_mut().spawn_search(test_path_like("a.t"), cx)
|
||||||
.spawn_search(FileSearchQuery::new("a.t".to_string()), cx)
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -884,8 +876,7 @@ mod tests {
|
|||||||
|
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| {
|
.update(cx, |f, cx| {
|
||||||
f.delegate_mut()
|
f.delegate_mut().spawn_search(test_path_like("a.txt"), cx)
|
||||||
.spawn_search(FileSearchQuery::new("a.txt".to_string()), cx)
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -928,8 +919,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| {
|
.update(cx, |f, cx| {
|
||||||
f.delegate_mut()
|
f.delegate_mut().spawn_search(test_path_like("dir"), cx)
|
||||||
.spawn_search(FileSearchQuery::new("dir".to_string()), cx)
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
@ -937,4 +927,18 @@ mod tests {
|
|||||||
assert_eq!(finder.delegate().matches.len(), 0);
|
assert_eq!(finder.delegate().matches.len(), 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_path_like(test_str: &str) -> PathLikeWithPosition<FileSearchQuery> {
|
||||||
|
PathLikeWithPosition::parse_str(test_str, |path_like_str| {
|
||||||
|
Ok::<_, std::convert::Infallible>(FileSearchQuery {
|
||||||
|
raw_query: test_str.to_owned(),
|
||||||
|
file_query_end: if path_like_str == test_str {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(path_like_str.len())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,3 +16,4 @@ settings = { path = "../settings" }
|
|||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
postage.workspace = true
|
postage.workspace = true
|
||||||
|
util = { path = "../util" }
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use editor::{
|
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
|
||||||
display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor, FILE_ROW_COLUMN_DELIMITER,
|
|
||||||
};
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Axis, Entity,
|
actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Axis, Entity,
|
||||||
View, ViewContext, ViewHandle,
|
View, ViewContext, ViewHandle,
|
||||||
@ -10,6 +8,7 @@ use gpui::{
|
|||||||
use menu::{Cancel, Confirm};
|
use menu::{Cancel, Confirm};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use text::{Bias, Point};
|
use text::{Bias, Point};
|
||||||
|
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||||
use workspace::{Modal, Workspace};
|
use workspace::{Modal, Workspace};
|
||||||
|
|
||||||
actions!(go_to_line, [Toggle]);
|
actions!(go_to_line, [Toggle]);
|
||||||
|
@ -70,3 +70,40 @@ pub fn compact(path: &Path) -> PathBuf {
|
|||||||
path.to_path_buf()
|
path.to_path_buf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const FILE_ROW_COLUMN_DELIMITER: char = ':';
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PathLikeWithPosition<P> {
|
||||||
|
pub path_like: P,
|
||||||
|
pub row: Option<u32>,
|
||||||
|
pub column: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> PathLikeWithPosition<P> {
|
||||||
|
pub fn parse_str<F, E>(s: &str, parse_path_like_str: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: Fn(&str) -> Result<P, E>,
|
||||||
|
{
|
||||||
|
let mut components = s.splitn(3, FILE_ROW_COLUMN_DELIMITER).map(str::trim).fuse();
|
||||||
|
let path_like_str = components.next().filter(|str| !str.is_empty());
|
||||||
|
let row = components.next().and_then(|row| row.parse::<u32>().ok());
|
||||||
|
let column = components
|
||||||
|
.next()
|
||||||
|
.filter(|_| row.is_some())
|
||||||
|
.and_then(|col| col.parse::<u32>().ok());
|
||||||
|
|
||||||
|
Ok(match path_like_str {
|
||||||
|
Some(path_like_str) => Self {
|
||||||
|
path_like: parse_path_like_str(path_like_str)?,
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
},
|
||||||
|
None => Self {
|
||||||
|
path_like: parse_path_like_str(s)?,
|
||||||
|
row: None,
|
||||||
|
column: None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user