@@ -35,7 +35,7 @@ use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{fmt::Display, mem, path::PathBuf, sync::Arc};
use ui::App;
-use util::{ResultExt, get_default_system_shell_preferring_bash};
+use util::{ResultExt, get_default_system_shell_preferring_bash, paths::PathStyle};
use uuid::Uuid;
#[derive(Debug)]
@@ -95,9 +95,14 @@ pub enum AssistantMessageChunk {
}
impl AssistantMessageChunk {
- pub fn from_str(chunk: &str, language_registry: &Arc<LanguageRegistry>, cx: &mut App) -> Self {
+ pub fn from_str(
+ chunk: &str,
+ language_registry: &Arc<LanguageRegistry>,
+ path_style: PathStyle,
+ cx: &mut App,
+ ) -> Self {
Self::Message {
- block: ContentBlock::new(chunk.into(), language_registry, cx),
+ block: ContentBlock::new(chunk.into(), language_registry, path_style, cx),
}
}
@@ -186,6 +191,7 @@ impl ToolCall {
tool_call: acp::ToolCall,
status: ToolCallStatus,
language_registry: Arc<LanguageRegistry>,
+ path_style: PathStyle,
terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
cx: &mut App,
) -> Result<Self> {
@@ -199,6 +205,7 @@ impl ToolCall {
content.push(ToolCallContent::from_acp(
item,
language_registry.clone(),
+ path_style,
terminals,
cx,
)?);
@@ -223,6 +230,7 @@ impl ToolCall {
&mut self,
fields: acp::ToolCallUpdateFields,
language_registry: Arc<LanguageRegistry>,
+ path_style: PathStyle,
terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
cx: &mut App,
) -> Result<()> {
@@ -260,12 +268,13 @@ impl ToolCall {
// Reuse existing content if we can
for (old, new) in self.content.iter_mut().zip(content.by_ref()) {
- old.update_from_acp(new, language_registry.clone(), terminals, cx)?;
+ old.update_from_acp(new, language_registry.clone(), path_style, terminals, cx)?;
}
for new in content {
self.content.push(ToolCallContent::from_acp(
new,
language_registry.clone(),
+ path_style,
terminals,
cx,
)?)
@@ -450,21 +459,23 @@ impl ContentBlock {
pub fn new(
block: acp::ContentBlock,
language_registry: &Arc<LanguageRegistry>,
+ path_style: PathStyle,
cx: &mut App,
) -> Self {
let mut this = Self::Empty;
- this.append(block, language_registry, cx);
+ this.append(block, language_registry, path_style, cx);
this
}
pub fn new_combined(
blocks: impl IntoIterator<Item = acp::ContentBlock>,
language_registry: Arc<LanguageRegistry>,
+ path_style: PathStyle,
cx: &mut App,
) -> Self {
let mut this = Self::Empty;
for block in blocks {
- this.append(block, &language_registry, cx);
+ this.append(block, &language_registry, path_style, cx);
}
this
}
@@ -473,6 +484,7 @@ impl ContentBlock {
&mut self,
block: acp::ContentBlock,
language_registry: &Arc<LanguageRegistry>,
+ path_style: PathStyle,
cx: &mut App,
) {
if matches!(self, ContentBlock::Empty)
@@ -482,7 +494,7 @@ impl ContentBlock {
return;
}
- let new_content = self.block_string_contents(block);
+ let new_content = self.block_string_contents(block, path_style);
match self {
ContentBlock::Empty => {
@@ -492,7 +504,7 @@ impl ContentBlock {
markdown.update(cx, |markdown, cx| markdown.append(&new_content, cx));
}
ContentBlock::ResourceLink { resource_link } => {
- let existing_content = Self::resource_link_md(&resource_link.uri);
+ let existing_content = Self::resource_link_md(&resource_link.uri, path_style);
let combined = format!("{}\n{}", existing_content, new_content);
*self = Self::create_markdown_block(combined, language_registry, cx);
@@ -511,11 +523,11 @@ impl ContentBlock {
}
}
- fn block_string_contents(&self, block: acp::ContentBlock) -> String {
+ fn block_string_contents(&self, block: acp::ContentBlock, path_style: PathStyle) -> String {
match block {
acp::ContentBlock::Text(text_content) => text_content.text,
acp::ContentBlock::ResourceLink(resource_link) => {
- Self::resource_link_md(&resource_link.uri)
+ Self::resource_link_md(&resource_link.uri, path_style)
}
acp::ContentBlock::Resource(acp::EmbeddedResource {
resource:
@@ -524,14 +536,14 @@ impl ContentBlock {
..
}),
..
- }) => Self::resource_link_md(&uri),
+ }) => Self::resource_link_md(&uri, path_style),
acp::ContentBlock::Image(image) => Self::image_md(&image),
acp::ContentBlock::Audio(_) | acp::ContentBlock::Resource(_) => String::new(),
}
}
- fn resource_link_md(uri: &str) -> String {
- if let Some(uri) = MentionUri::parse(uri).log_err() {
+ fn resource_link_md(uri: &str, path_style: PathStyle) -> String {
+ if let Some(uri) = MentionUri::parse(uri, path_style).log_err() {
uri.as_link().to_string()
} else {
uri.to_string()
@@ -577,6 +589,7 @@ impl ToolCallContent {
pub fn from_acp(
content: acp::ToolCallContent,
language_registry: Arc<LanguageRegistry>,
+ path_style: PathStyle,
terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
cx: &mut App,
) -> Result<Self> {
@@ -584,6 +597,7 @@ impl ToolCallContent {
acp::ToolCallContent::Content { content } => Ok(Self::ContentBlock(ContentBlock::new(
content,
&language_registry,
+ path_style,
cx,
))),
acp::ToolCallContent::Diff { diff } => Ok(Self::Diff(cx.new(|cx| {
@@ -607,6 +621,7 @@ impl ToolCallContent {
&mut self,
new: acp::ToolCallContent,
language_registry: Arc<LanguageRegistry>,
+ path_style: PathStyle,
terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
cx: &mut App,
) -> Result<()> {
@@ -622,7 +637,7 @@ impl ToolCallContent {
};
if needs_update {
- *self = Self::from_acp(new, language_registry, terminals, cx)?;
+ *self = Self::from_acp(new, language_registry, path_style, terminals, cx)?;
}
Ok(())
}
@@ -1142,6 +1157,7 @@ impl AcpThread {
cx: &mut Context<Self>,
) {
let language_registry = self.project.read(cx).languages().clone();
+ let path_style = self.project.read(cx).path_style(cx);
let entries_len = self.entries.len();
if let Some(last_entry) = self.entries.last_mut()
@@ -1153,12 +1169,12 @@ impl AcpThread {
}) = last_entry
{
*id = message_id.or(id.take());
- content.append(chunk.clone(), &language_registry, cx);
+ content.append(chunk.clone(), &language_registry, path_style, cx);
chunks.push(chunk);
let idx = entries_len - 1;
cx.emit(AcpThreadEvent::EntryUpdated(idx));
} else {
- let content = ContentBlock::new(chunk.clone(), &language_registry, cx);
+ let content = ContentBlock::new(chunk.clone(), &language_registry, path_style, cx);
self.push_entry(
AgentThreadEntry::UserMessage(UserMessage {
id: message_id,
@@ -1178,6 +1194,7 @@ impl AcpThread {
cx: &mut Context<Self>,
) {
let language_registry = self.project.read(cx).languages().clone();
+ let path_style = self.project.read(cx).path_style(cx);
let entries_len = self.entries.len();
if let Some(last_entry) = self.entries.last_mut()
&& let AgentThreadEntry::AssistantMessage(AssistantMessage { chunks }) = last_entry
@@ -1187,10 +1204,10 @@ impl AcpThread {
match (chunks.last_mut(), is_thought) {
(Some(AssistantMessageChunk::Message { block }), false)
| (Some(AssistantMessageChunk::Thought { block }), true) => {
- block.append(chunk, &language_registry, cx)
+ block.append(chunk, &language_registry, path_style, cx)
}
_ => {
- let block = ContentBlock::new(chunk, &language_registry, cx);
+ let block = ContentBlock::new(chunk, &language_registry, path_style, cx);
if is_thought {
chunks.push(AssistantMessageChunk::Thought { block })
} else {
@@ -1199,7 +1216,7 @@ impl AcpThread {
}
}
} else {
- let block = ContentBlock::new(chunk, &language_registry, cx);
+ let block = ContentBlock::new(chunk, &language_registry, path_style, cx);
let chunk = if is_thought {
AssistantMessageChunk::Thought { block }
} else {
@@ -1251,6 +1268,7 @@ impl AcpThread {
) -> Result<()> {
let update = update.into();
let languages = self.project.read(cx).languages().clone();
+ let path_style = self.project.read(cx).path_style(cx);
let ix = match self.index_for_tool_call(update.id()) {
Some(ix) => ix,
@@ -1267,6 +1285,7 @@ impl AcpThread {
meta: None,
}),
&languages,
+ path_style,
cx,
))],
status: ToolCallStatus::Failed,
@@ -1286,7 +1305,7 @@ impl AcpThread {
match update {
ToolCallUpdate::UpdateFields(update) => {
let location_updated = update.fields.locations.is_some();
- call.update_fields(update.fields, languages, &self.terminals, cx)?;
+ call.update_fields(update.fields, languages, path_style, &self.terminals, cx)?;
if location_updated {
self.resolve_locations(update.id, cx);
}
@@ -1325,6 +1344,7 @@ impl AcpThread {
cx: &mut Context<Self>,
) -> Result<(), acp::Error> {
let language_registry = self.project.read(cx).languages().clone();
+ let path_style = self.project.read(cx).path_style(cx);
let id = update.id.clone();
if let Some(ix) = self.index_for_tool_call(&id) {
@@ -1332,7 +1352,13 @@ impl AcpThread {
unreachable!()
};
- call.update_fields(update.fields, language_registry, &self.terminals, cx)?;
+ call.update_fields(
+ update.fields,
+ language_registry,
+ path_style,
+ &self.terminals,
+ cx,
+ )?;
call.status = status;
cx.emit(AcpThreadEvent::EntryUpdated(ix));
@@ -1341,6 +1367,7 @@ impl AcpThread {
update.try_into()?,
status,
language_registry,
+ self.project.read(cx).path_style(cx),
&self.terminals,
cx,
)?;
@@ -1620,6 +1647,7 @@ impl AcpThread {
let block = ContentBlock::new_combined(
message.clone(),
self.project.read(cx).languages().clone(),
+ self.project.read(cx).path_style(cx),
cx,
);
let request = acp::PromptRequest {
@@ -7,10 +7,10 @@ use std::{
fmt,
ops::RangeInclusive,
path::{Path, PathBuf},
- str::FromStr,
};
use ui::{App, IconName, SharedString};
use url::Url;
+use util::paths::PathStyle;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub enum MentionUri {
@@ -49,7 +49,7 @@ pub enum MentionUri {
}
impl MentionUri {
- pub fn parse(input: &str) -> Result<Self> {
+ pub fn parse(input: &str, path_style: PathStyle) -> Result<Self> {
fn parse_line_range(fragment: &str) -> Result<RangeInclusive<u32>> {
let range = fragment
.strip_prefix("L")
@@ -74,25 +74,34 @@ impl MentionUri {
let path = url.path();
match url.scheme() {
"file" => {
- let path = url.to_file_path().ok().context("Extracting file path")?;
+ let path = if path_style.is_windows() {
+ path.trim_start_matches("/")
+ } else {
+ path
+ };
+
if let Some(fragment) = url.fragment() {
let line_range = parse_line_range(fragment)?;
if let Some(name) = single_query_param(&url, "symbol")? {
Ok(Self::Symbol {
name,
- abs_path: path,
+ abs_path: path.into(),
line_range,
})
} else {
Ok(Self::Selection {
- abs_path: Some(path),
+ abs_path: Some(path.into()),
line_range,
})
}
} else if input.ends_with("/") {
- Ok(Self::Directory { abs_path: path })
+ Ok(Self::Directory {
+ abs_path: path.into(),
+ })
} else {
- Ok(Self::File { abs_path: path })
+ Ok(Self::File {
+ abs_path: path.into(),
+ })
}
}
"zed" => {
@@ -213,18 +222,14 @@ impl MentionUri {
pub fn to_uri(&self) -> Url {
match self {
MentionUri::File { abs_path } => {
- let mut url = Url::parse("zed:///").unwrap();
- url.set_path("/agent/file");
- url.query_pairs_mut()
- .append_pair("path", &abs_path.to_string_lossy());
+ let mut url = Url::parse("file:///").unwrap();
+ url.set_path(&abs_path.to_string_lossy());
url
}
MentionUri::PastedImage => Url::parse("zed:///agent/pasted-image").unwrap(),
MentionUri::Directory { abs_path } => {
- let mut url = Url::parse("zed:///").unwrap();
- url.set_path("/agent/directory");
- url.query_pairs_mut()
- .append_pair("path", &abs_path.to_string_lossy());
+ let mut url = Url::parse("file:///").unwrap();
+ url.set_path(&abs_path.to_string_lossy());
url
}
MentionUri::Symbol {
@@ -232,10 +237,9 @@ impl MentionUri {
name,
line_range,
} => {
- let mut url = Url::parse("zed:///").unwrap();
- url.set_path(&format!("/agent/symbol/{name}"));
- url.query_pairs_mut()
- .append_pair("path", &abs_path.to_string_lossy());
+ let mut url = Url::parse("file:///").unwrap();
+ url.set_path(&abs_path.to_string_lossy());
+ url.query_pairs_mut().append_pair("symbol", name);
url.set_fragment(Some(&format!(
"L{}:{}",
line_range.start() + 1,
@@ -247,13 +251,14 @@ impl MentionUri {
abs_path,
line_range,
} => {
- let mut url = Url::parse("zed:///").unwrap();
- if let Some(abs_path) = abs_path {
- url.set_path("/agent/selection");
- url.query_pairs_mut()
- .append_pair("path", &abs_path.to_string_lossy());
+ let mut url = if let Some(path) = abs_path {
+ let mut url = Url::parse("file:///").unwrap();
+ url.set_path(&path.to_string_lossy());
+ url
} else {
+ let mut url = Url::parse("zed:///").unwrap();
url.set_path("/agent/untitled-buffer");
+ url
};
url.set_fragment(Some(&format!(
"L{}:{}",
@@ -288,14 +293,6 @@ impl MentionUri {
}
}
-impl FromStr for MentionUri {
- type Err = anyhow::Error;
-
- fn from_str(s: &str) -> anyhow::Result<Self> {
- Self::parse(s)
- }
-}
-
pub struct MentionLink<'a>(&'a MentionUri);
impl fmt::Display for MentionLink<'_> {
@@ -338,93 +335,81 @@ mod tests {
#[test]
fn test_parse_file_uri() {
- let old_uri = uri!("file:///path/to/file.rs");
- let parsed = MentionUri::parse(old_uri).unwrap();
+ let file_uri = uri!("file:///path/to/file.rs");
+ let parsed = MentionUri::parse(file_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::File { abs_path } => {
- assert_eq!(abs_path.to_str().unwrap(), path!("/path/to/file.rs"));
+ assert_eq!(abs_path, Path::new(path!("/path/to/file.rs")));
}
_ => panic!("Expected File variant"),
}
- let new_uri = parsed.to_uri().to_string();
- assert!(new_uri.starts_with("zed:///agent/file"));
- assert_eq!(MentionUri::parse(&new_uri).unwrap(), parsed);
+ assert_eq!(parsed.to_uri().to_string(), file_uri);
}
#[test]
fn test_parse_directory_uri() {
- let old_uri = uri!("file:///path/to/dir/");
- let parsed = MentionUri::parse(old_uri).unwrap();
+ let file_uri = uri!("file:///path/to/dir/");
+ let parsed = MentionUri::parse(file_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Directory { abs_path } => {
- assert_eq!(abs_path.to_str().unwrap(), path!("/path/to/dir/"));
+ assert_eq!(abs_path, Path::new(path!("/path/to/dir/")));
}
_ => panic!("Expected Directory variant"),
}
- let new_uri = parsed.to_uri().to_string();
- assert!(new_uri.starts_with("zed:///agent/directory"));
- assert_eq!(MentionUri::parse(&new_uri).unwrap(), parsed);
+ assert_eq!(parsed.to_uri().to_string(), file_uri);
}
#[test]
fn test_to_directory_uri_without_slash() {
let uri = MentionUri::Directory {
- abs_path: PathBuf::from(path!("/path/to/dir")),
+ abs_path: PathBuf::from(path!("/path/to/dir/")),
};
- let uri_string = uri.to_uri().to_string();
- assert!(uri_string.starts_with("zed:///agent/directory"));
- assert_eq!(MentionUri::parse(&uri_string).unwrap(), uri);
+ let expected = uri!("file:///path/to/dir/");
+ assert_eq!(uri.to_uri().to_string(), expected);
}
#[test]
fn test_parse_symbol_uri() {
- let old_uri = uri!("file:///path/to/file.rs?symbol=MySymbol#L10:20");
- let parsed = MentionUri::parse(old_uri).unwrap();
+ let symbol_uri = uri!("file:///path/to/file.rs?symbol=MySymbol#L10:20");
+ let parsed = MentionUri::parse(symbol_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Symbol {
abs_path: path,
name,
line_range,
} => {
- assert_eq!(path.to_str().unwrap(), path!("/path/to/file.rs"));
+ assert_eq!(path, Path::new(path!("/path/to/file.rs")));
assert_eq!(name, "MySymbol");
assert_eq!(line_range.start(), &9);
assert_eq!(line_range.end(), &19);
}
_ => panic!("Expected Symbol variant"),
}
- let new_uri = parsed.to_uri().to_string();
- assert!(new_uri.starts_with("zed:///agent/symbol/MySymbol"));
- assert_eq!(MentionUri::parse(&new_uri).unwrap(), parsed);
+ assert_eq!(parsed.to_uri().to_string(), symbol_uri);
}
#[test]
fn test_parse_selection_uri() {
- let old_uri = uri!("file:///path/to/file.rs#L5:15");
- let parsed = MentionUri::parse(old_uri).unwrap();
+ let selection_uri = uri!("file:///path/to/file.rs#L5:15");
+ let parsed = MentionUri::parse(selection_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Selection {
abs_path: path,
line_range,
} => {
- assert_eq!(
- path.as_ref().unwrap().to_str().unwrap(),
- path!("/path/to/file.rs")
- );
+ assert_eq!(path.as_ref().unwrap(), Path::new(path!("/path/to/file.rs")));
assert_eq!(line_range.start(), &4);
assert_eq!(line_range.end(), &14);
}
_ => panic!("Expected Selection variant"),
}
- let new_uri = parsed.to_uri().to_string();
- assert!(new_uri.starts_with("zed:///agent/selection"));
- assert_eq!(MentionUri::parse(&new_uri).unwrap(), parsed);
+ assert_eq!(parsed.to_uri().to_string(), selection_uri);
}
#[test]
fn test_parse_untitled_selection_uri() {
let selection_uri = uri!("zed:///agent/untitled-buffer#L1:10");
- let parsed = MentionUri::parse(selection_uri).unwrap();
+ let parsed = MentionUri::parse(selection_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Selection {
abs_path: None,
@@ -441,7 +426,7 @@ mod tests {
#[test]
fn test_parse_thread_uri() {
let thread_uri = "zed:///agent/thread/session123?name=Thread+name";
- let parsed = MentionUri::parse(thread_uri).unwrap();
+ let parsed = MentionUri::parse(thread_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Thread {
id: thread_id,
@@ -458,7 +443,7 @@ mod tests {
#[test]
fn test_parse_rule_uri() {
let rule_uri = "zed:///agent/rule/d8694ff2-90d5-4b6f-be33-33c1763acd52?name=Some+rule";
- let parsed = MentionUri::parse(rule_uri).unwrap();
+ let parsed = MentionUri::parse(rule_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Rule { id, name } => {
assert_eq!(id.to_string(), "d8694ff2-90d5-4b6f-be33-33c1763acd52");
@@ -472,7 +457,7 @@ mod tests {
#[test]
fn test_parse_fetch_http_uri() {
let http_uri = "http://example.com/path?query=value#fragment";
- let parsed = MentionUri::parse(http_uri).unwrap();
+ let parsed = MentionUri::parse(http_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Fetch { url } => {
assert_eq!(url.to_string(), http_uri);
@@ -485,7 +470,7 @@ mod tests {
#[test]
fn test_parse_fetch_https_uri() {
let https_uri = "https://example.com/api/endpoint";
- let parsed = MentionUri::parse(https_uri).unwrap();
+ let parsed = MentionUri::parse(https_uri, PathStyle::local()).unwrap();
match &parsed {
MentionUri::Fetch { url } => {
assert_eq!(url.to_string(), https_uri);
@@ -497,40 +482,55 @@ mod tests {
#[test]
fn test_invalid_scheme() {
- assert!(MentionUri::parse("ftp://example.com").is_err());
- assert!(MentionUri::parse("ssh://example.com").is_err());
- assert!(MentionUri::parse("unknown://example.com").is_err());
+ assert!(MentionUri::parse("ftp://example.com", PathStyle::local()).is_err());
+ assert!(MentionUri::parse("ssh://example.com", PathStyle::local()).is_err());
+ assert!(MentionUri::parse("unknown://example.com", PathStyle::local()).is_err());
}
#[test]
fn test_invalid_zed_path() {
- assert!(MentionUri::parse("zed:///invalid/path").is_err());
- assert!(MentionUri::parse("zed:///agent/unknown/test").is_err());
+ assert!(MentionUri::parse("zed:///invalid/path", PathStyle::local()).is_err());
+ assert!(MentionUri::parse("zed:///agent/unknown/test", PathStyle::local()).is_err());
}
#[test]
fn test_invalid_line_range_format() {
// Missing L prefix
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#10:20")).is_err());
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#10:20"), PathStyle::local()).is_err()
+ );
// Missing colon separator
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#L1020")).is_err());
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#L1020"), PathStyle::local()).is_err()
+ );
// Invalid numbers
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#L10:abc")).is_err());
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#Labc:20")).is_err());
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#L10:abc"), PathStyle::local()).is_err()
+ );
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#Labc:20"), PathStyle::local()).is_err()
+ );
}
#[test]
fn test_invalid_query_parameters() {
// Invalid query parameter name
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#L10:20?invalid=test")).is_err());
+ assert!(
+ MentionUri::parse(
+ uri!("file:///path/to/file.rs#L10:20?invalid=test"),
+ PathStyle::local()
+ )
+ .is_err()
+ );
// Too many query parameters
assert!(
- MentionUri::parse(uri!(
- "file:///path/to/file.rs#L10:20?symbol=test&another=param"
- ))
+ MentionUri::parse(
+ uri!("file:///path/to/file.rs#L10:20?symbol=test&another=param"),
+ PathStyle::local()
+ )
.is_err()
);
}
@@ -538,8 +538,14 @@ mod tests {
#[test]
fn test_zero_based_line_numbers() {
// Test that 0-based line numbers are rejected (should be 1-based)
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#L0:10")).is_err());
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#L1:0")).is_err());
- assert!(MentionUri::parse(uri!("file:///path/to/file.rs#L0:0")).is_err());
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#L0:10"), PathStyle::local()).is_err()
+ );
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#L1:0"), PathStyle::local()).is_err()
+ );
+ assert!(
+ MentionUri::parse(uri!("file:///path/to/file.rs#L0:0"), PathStyle::local()).is_err()
+ );
}
}