Detailed changes
@@ -18241,6 +18241,7 @@ dependencies = [
"log",
"mach2 0.5.0",
"nix 0.29.0",
+ "percent-encoding",
"pretty_assertions",
"rand 0.9.2",
"regex",
@@ -18255,6 +18256,7 @@ dependencies = [
"tempfile",
"tendril",
"unicase",
+ "url",
"util_macros",
"walkdir",
"which 6.0.3",
@@ -596,6 +596,7 @@ partial-json-fixer = "0.5.3"
parse_int = "0.9"
pciid-parser = "0.8.0"
pathdiff = "0.2"
+percent-encoding = "2.3.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "d5b5bb0c4558a51d8cc76b514bc870fd1c042f16" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "d5b5bb0c4558a51d8cc76b514bc870fd1c042f16" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "d5b5bb0c4558a51d8cc76b514bc870fd1c042f16" }
@@ -2631,6 +2631,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap();
builder.subscribe(cx)
@@ -2705,6 +2706,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap();
builder.subscribe(cx)
@@ -2791,6 +2793,7 @@ mod tests {
Some(completion_tx),
cx,
vec![],
+ PathStyle::local(),
)
})
.await
@@ -4080,6 +4083,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap();
builder.subscribe(cx)
@@ -4126,6 +4130,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap();
builder.subscribe(cx)
@@ -4186,6 +4191,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap();
builder.subscribe(cx)
@@ -1284,6 +1284,7 @@ impl acp::Client for ClientDelegate {
None,
0,
cx.background_executor(),
+ thread.project().read(cx).path_style(cx),
)?;
let lower = cx.new(|cx| builder.subscribe(cx));
thread.on_terminal_provider_event(
@@ -1,11 +1,10 @@
-use std::path::PathBuf;
-
use anyhow::Context as _;
use gpui::{App, Context, Entity, Window};
use language::Language;
use project::lsp_store::lsp_ext_command::SwitchSourceHeaderResult;
use rpc::proto;
-use util::paths::PathStyle;
+use url::Url;
+use util::paths::{PathStyle, UrlExt as _};
use workspace::{OpenOptions, OpenVisible};
use crate::lsp_ext::find_specific_language_server_in_selection;
@@ -77,25 +76,22 @@ pub fn switch_source_header(
if switch_source_header.0.is_empty() {
return Ok(());
}
-
- let goto = switch_source_header
- .0
- .strip_prefix("file://")
- .with_context(|| {
- format!(
- "Parsing file url \"{}\" returned from switch source/header failed",
- switch_source_header.0
- )
- })?;
+ let path_style = workspace.update(cx, |ws, cx| ws.path_style(cx));
+ let path = Url::parse(&switch_source_header.0).with_context(|| {
+ format!(
+ "Parsing URL \"{}\" returned from switch source/header failed",
+ switch_source_header.0
+ )
+ })?;
+ let path = path.to_file_path_ext(path_style).map_err(|()| {
+ anyhow::anyhow!(
+ "URL conversion to file path failed for \"{}\"",
+ switch_source_header.0
+ )
+ })?;
workspace
.update_in(cx, |workspace, window, cx| {
- let goto = if workspace.path_style(cx).is_windows() {
- goto.strip_prefix('/').unwrap_or(goto)
- } else {
- goto
- };
- let path = PathBuf::from(goto);
workspace.open_abs_path(
path,
OpenOptions {
@@ -107,7 +103,10 @@ pub fn switch_source_header(
)
})
.with_context(|| {
- format!("Switch source/header could not open \"{goto}\" in workspace")
+ format!(
+ "Switch source/header could not open \"{}\" in workspace",
+ switch_source_header.0
+ )
})?
.await
.map(|_| ())
@@ -91,8 +91,8 @@ impl Project {
.unwrap_or_else(get_default_system_shell),
None => settings.shell.program(),
};
- let is_windows = self.path_style(cx).is_windows();
- let shell_kind = ShellKind::new(&shell, is_windows);
+ let path_style = self.path_style(cx);
+ let shell_kind = ShellKind::new(&shell, path_style.is_windows());
// Prepare a task for resolving the environment
let env_task =
@@ -248,6 +248,7 @@ impl Project {
Some(completion_tx),
cx,
activation_script,
+ path_style,
))
})??
.await?;
@@ -356,7 +357,7 @@ impl Project {
None => settings.shell.program(),
};
- let is_windows = self.path_style(cx).is_windows();
+ let path_style = self.path_style(cx);
// Prepare a task for resolving the environment
let env_task =
@@ -364,7 +365,7 @@ impl Project {
let lang_registry = self.languages.clone();
cx.spawn(async move |project, cx| {
- let shell_kind = ShellKind::new(&shell, is_windows);
+ let shell_kind = ShellKind::new(&shell, path_style.is_windows());
let mut env = env_task.await.unwrap_or_default();
env.extend(settings.env);
@@ -412,6 +413,7 @@ impl Project {
None,
cx,
activation_script,
+ path_style,
))
})??
.await?;
@@ -50,7 +50,7 @@ use terminal_hyperlinks::RegexSearches;
use terminal_settings::{AlternateScroll, CursorShape, TerminalSettings};
use theme::{ActiveTheme, Theme};
use urlencoding;
-use util::truncate_and_trailoff;
+use util::{paths::PathStyle, truncate_and_trailoff};
use std::{
borrow::Cow,
@@ -347,6 +347,7 @@ impl TerminalBuilder {
max_scroll_history_lines: Option<usize>,
window_id: u64,
background_executor: &BackgroundExecutor,
+ path_style: PathStyle,
) -> Result<TerminalBuilder> {
// Create a display-only terminal (no actual PTY).
let default_cursor_style = AlacCursorStyle::from(cursor_shape);
@@ -411,6 +412,7 @@ impl TerminalBuilder {
child_exited: None,
event_loop_task: Task::ready(Ok(())),
background_executor: background_executor.clone(),
+ path_style,
};
Ok(TerminalBuilder {
@@ -434,6 +436,7 @@ impl TerminalBuilder {
completion_tx: Option<Sender<Option<ExitStatus>>>,
cx: &App,
activation_script: Vec<String>,
+ path_style: PathStyle,
) -> Task<Result<TerminalBuilder>> {
let version = release_channel::AppVersion::global(cx);
let background_executor = cx.background_executor().clone();
@@ -640,6 +643,7 @@ impl TerminalBuilder {
child_exited: None,
event_loop_task: Task::ready(Ok(())),
background_executor,
+ path_style,
};
if !activation_script.is_empty() && no_task {
@@ -863,6 +867,7 @@ pub struct Terminal {
child_exited: Option<ExitStatus>,
event_loop_task: Task<Result<(), anyhow::Error>>,
background_executor: BackgroundExecutor,
+ path_style: PathStyle,
}
struct CopyTemplate {
@@ -1181,6 +1186,7 @@ impl Terminal {
term,
point,
&mut self.hyperlink_regex_searches,
+ self.path_style,
) {
Some(hyperlink) => {
self.process_hyperlink(hyperlink, *open, cx);
@@ -1869,6 +1875,7 @@ impl Terminal {
&term_lock,
point,
&mut self.hyperlink_regex_searches,
+ self.path_style,
);
drop(term_lock);
@@ -1960,6 +1967,7 @@ impl Terminal {
&term_lock,
point,
&mut self.hyperlink_regex_searches,
+ self.path_style,
)
} {
if mouse_down_hyperlink == mouse_up_hyperlink {
@@ -2283,6 +2291,7 @@ impl Terminal {
None,
cx,
self.activation_script.clone(),
+ self.path_style,
)
}
}
@@ -2553,6 +2562,7 @@ mod tests {
Some(completion_tx),
cx,
vec![],
+ PathStyle::local(),
)
})
.await
@@ -2574,6 +2584,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap()
.subscribe(cx)
@@ -2697,6 +2708,7 @@ mod tests {
Some(completion_tx),
cx,
Vec::new(),
+ PathStyle::local(),
)
})
.await
@@ -2772,6 +2784,7 @@ mod tests {
Some(completion_tx),
cx,
Vec::new(),
+ PathStyle::local(),
)
})
.await
@@ -2958,6 +2971,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap()
.subscribe(cx)
@@ -3005,6 +3019,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap()
.subscribe(cx)
@@ -3046,6 +3061,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.unwrap()
.subscribe(cx)
@@ -3256,6 +3272,7 @@ mod tests {
None,
cx,
vec![],
+ PathStyle::local(),
)
})
.await
@@ -16,6 +16,7 @@ use std::{
time::{Duration, Instant},
};
use url::Url;
+use util::paths::{PathStyle, UrlExt};
const URL_REGEX: &str = r#"(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file://|git://|ssh:|ftp://)[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>"\s{-}\^⟨⟩`']+"#;
const WIDE_CHAR_SPACERS: Flags =
@@ -70,6 +71,7 @@ pub(super) fn find_from_grid_point<T: EventListener>(
term: &Term<T>,
point: AlacPoint,
regex_searches: &mut RegexSearches,
+ path_style: PathStyle,
) -> Option<(String, bool, Match)> {
let grid = term.grid();
let link = grid.index(point).hyperlink();
@@ -135,7 +137,7 @@ pub(super) fn find_from_grid_point<T: EventListener>(
// (e.g., file:///C:/path -> C:\path)
if maybe_url_or_path.starts_with("file://") {
if let Ok(url) = Url::parse(&maybe_url_or_path) {
- if let Ok(path) = url.to_file_path() {
+ if let Ok(path) = url.to_file_path_ext(path_style) {
return (path.to_string_lossy().into_owned(), false, word_match);
}
}
@@ -1217,7 +1219,12 @@ mod tests {
}
TEST_REGEX_SEARCHES.with(|regex_searches| {
- find_from_grid_point(&term, point, &mut regex_searches.borrow_mut())
+ find_from_grid_point(
+ &term,
+ point,
+ &mut regex_searches.borrow_mut(),
+ PathStyle::local(),
+ )
})
}
}
@@ -1819,6 +1826,7 @@ mod tests {
&term,
expected_hyperlink.hovered_grid_point,
&mut regex_searches.borrow_mut(),
+ PathStyle::local(),
)
});
let check_hyperlink_match =
@@ -562,6 +562,7 @@ mod tests {
None,
0,
cx.background_executor(),
+ PathStyle::local(),
)
.expect("Failed to create display-only terminal")
.subscribe(cx)
@@ -42,6 +42,8 @@ smol.workspace = true
take-until.workspace = true
tempfile.workspace = true
unicase.workspace = true
+url.workspace = true
+percent-encoding.workspace = true
util_macros = { workspace = true, optional = true }
walkdir.workspace = true
which.workspace = true
@@ -1407,6 +1407,153 @@ impl WslPath {
}
}
+pub trait UrlExt {
+ /// A version of `url::Url::to_file_path` that does platform handling based on the provided `PathStyle` instead of the host platform.
+ ///
+ /// Prefer using this over `url::Url::to_file_path` when you need to handle paths in a cross-platform way as is the case for remoting interactions.
+ fn to_file_path_ext(&self, path_style: PathStyle) -> Result<PathBuf, ()>;
+}
+
+impl UrlExt for url::Url {
+ // Copied from `url::Url::to_file_path`, but the `cfg` handling is replaced with runtime branching on `PathStyle`
+ fn to_file_path_ext(&self, source_path_style: PathStyle) -> Result<PathBuf, ()> {
+ if let Some(segments) = self.path_segments() {
+ let host = match self.host() {
+ None | Some(url::Host::Domain("localhost")) => None,
+ Some(_) if source_path_style.is_windows() && self.scheme() == "file" => {
+ self.host_str()
+ }
+ _ => return Err(()),
+ };
+
+ let str_len = self.as_str().len();
+ let estimated_capacity = if source_path_style.is_windows() {
+ // remove scheme: - has possible \\ for hostname
+ str_len.saturating_sub(self.scheme().len() + 1)
+ } else {
+ // remove scheme://
+ str_len.saturating_sub(self.scheme().len() + 3)
+ };
+ return match source_path_style {
+ PathStyle::Posix => {
+ file_url_segments_to_pathbuf_posix(estimated_capacity, host, segments)
+ }
+ PathStyle::Windows => {
+ file_url_segments_to_pathbuf_windows(estimated_capacity, host, segments)
+ }
+ };
+ }
+
+ fn file_url_segments_to_pathbuf_posix(
+ estimated_capacity: usize,
+ host: Option<&str>,
+ segments: std::str::Split<'_, char>,
+ ) -> Result<PathBuf, ()> {
+ use percent_encoding::percent_decode;
+
+ if host.is_some() {
+ return Err(());
+ }
+
+ let mut bytes = Vec::new();
+ bytes.try_reserve(estimated_capacity).map_err(|_| ())?;
+
+ for segment in segments {
+ bytes.push(b'/');
+ bytes.extend(percent_decode(segment.as_bytes()));
+ }
+
+ // A windows drive letter must end with a slash.
+ if bytes.len() > 2
+ && bytes[bytes.len() - 2].is_ascii_alphabetic()
+ && matches!(bytes[bytes.len() - 1], b':' | b'|')
+ {
+ bytes.push(b'/');
+ }
+
+ let path = String::from_utf8(bytes).map_err(|_| ())?;
+ debug_assert!(
+ PathStyle::Posix.is_absolute(&path),
+ "to_file_path() failed to produce an absolute Path"
+ );
+
+ Ok(PathBuf::from(path))
+ }
+
+ fn file_url_segments_to_pathbuf_windows(
+ estimated_capacity: usize,
+ host: Option<&str>,
+ mut segments: std::str::Split<'_, char>,
+ ) -> Result<PathBuf, ()> {
+ use percent_encoding::percent_decode_str;
+ let mut string = String::new();
+ string.try_reserve(estimated_capacity).map_err(|_| ())?;
+ if let Some(host) = host {
+ string.push_str(r"\\");
+ string.push_str(host);
+ } else {
+ let first = segments.next().ok_or(())?;
+
+ match first.len() {
+ 2 => {
+ if !first.starts_with(|c| char::is_ascii_alphabetic(&c))
+ || first.as_bytes()[1] != b':'
+ {
+ return Err(());
+ }
+
+ string.push_str(first);
+ }
+
+ 4 => {
+ if !first.starts_with(|c| char::is_ascii_alphabetic(&c)) {
+ return Err(());
+ }
+ let bytes = first.as_bytes();
+ if bytes[1] != b'%'
+ || bytes[2] != b'3'
+ || (bytes[3] != b'a' && bytes[3] != b'A')
+ {
+ return Err(());
+ }
+
+ string.push_str(&first[0..1]);
+ string.push(':');
+ }
+
+ _ => return Err(()),
+ }
+ };
+
+ for segment in segments {
+ string.push('\\');
+
+ // Currently non-unicode windows paths cannot be represented
+ match percent_decode_str(segment).decode_utf8() {
+ Ok(s) => string.push_str(&s),
+ Err(..) => return Err(()),
+ }
+ }
+ // ensure our estimated capacity was good
+ if cfg!(test) {
+ debug_assert!(
+ string.len() <= estimated_capacity,
+ "len: {}, capacity: {}",
+ string.len(),
+ estimated_capacity
+ );
+ }
+ debug_assert!(
+ PathStyle::Windows.is_absolute(&string),
+ "to_file_path() failed to produce an absolute Path"
+ );
+ let path = PathBuf::from(string);
+ Ok(path)
+ }
+ Err(())
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::rel_path::rel_path;
@@ -2702,4 +2849,213 @@ mod tests {
let path = r"\\windows.localhost\Distro\foo";
assert_eq!(WslPath::from_path(&path), None);
}
+
+ #[test]
+ fn test_url_to_file_path_ext_posix_basic() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///home/user/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/home/user/file.txt"))
+ );
+
+ let url = url::Url::parse("file:///").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/"))
+ );
+
+ let url = url::Url::parse("file:///a/b/c/d/e").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/a/b/c/d/e"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_posix_percent_encoding() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///home/user/file%20with%20spaces.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/home/user/file with spaces.txt"))
+ );
+
+ let url = url::Url::parse("file:///path%2Fwith%2Fencoded%2Fslashes").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/path/with/encoded/slashes"))
+ );
+
+ let url = url::Url::parse("file:///special%23chars%3F.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/special#chars?.txt"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_posix_localhost() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file://localhost/home/user/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/home/user/file.txt"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_posix_rejects_host() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file://somehost/home/user/file.txt").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Posix), Err(()));
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_posix_windows_drive_letter() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///C:").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/C:/"))
+ );
+
+ let url = url::Url::parse("file:///D|").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Posix),
+ Ok(PathBuf::from("/D|/"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_windows_basic() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///C:/Users/user/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("C:\\Users\\user\\file.txt"))
+ );
+
+ let url = url::Url::parse("file:///D:/folder/subfolder/file.rs").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("D:\\folder\\subfolder\\file.rs"))
+ );
+
+ let url = url::Url::parse("file:///C:/").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("C:\\"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_windows_encoded_drive_letter() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///C%3A/Users/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("C:\\Users\\file.txt"))
+ );
+
+ let url = url::Url::parse("file:///c%3a/Users/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("c:\\Users\\file.txt"))
+ );
+
+ let url = url::Url::parse("file:///D%3A/folder/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("D:\\folder\\file.txt"))
+ );
+
+ let url = url::Url::parse("file:///d%3A/folder/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("d:\\folder\\file.txt"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_windows_unc_path() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file://server/share/path/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("\\\\server\\share\\path\\file.txt"))
+ );
+
+ let url = url::Url::parse("file://server/share").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("\\\\server\\share"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_windows_percent_encoding() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///C:/Users/user/file%20with%20spaces.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("C:\\Users\\user\\file with spaces.txt"))
+ );
+
+ let url = url::Url::parse("file:///C:/special%23chars%3F.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("C:\\special#chars?.txt"))
+ );
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_windows_invalid_drive() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file:///1:/path/file.txt").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Windows), Err(()));
+
+ let url = url::Url::parse("file:///CC:/path/file.txt").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Windows), Err(()));
+
+ let url = url::Url::parse("file:///C/path/file.txt").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Windows), Err(()));
+
+ let url = url::Url::parse("file:///invalid").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Windows), Err(()));
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_non_file_scheme() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("http://example.com/path").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Posix), Err(()));
+ assert_eq!(url.to_file_path_ext(PathStyle::Windows), Err(()));
+
+ let url = url::Url::parse("https://example.com/path").unwrap();
+ assert_eq!(url.to_file_path_ext(PathStyle::Posix), Err(()));
+ assert_eq!(url.to_file_path_ext(PathStyle::Windows), Err(()));
+ }
+
+ #[test]
+ fn test_url_to_file_path_ext_windows_localhost() {
+ use super::UrlExt;
+
+ let url = url::Url::parse("file://localhost/C:/Users/file.txt").unwrap();
+ assert_eq!(
+ url.to_file_path_ext(PathStyle::Windows),
+ Ok(PathBuf::from("C:\\Users\\file.txt"))
+ );
+ }
}