1use std::collections::VecDeque;
2pub use std::path::{Path, PathBuf};
3pub use std::sync::Arc;
4
5use anyhow::Context;
6use fs::Fs;
7use gpui::ModelHandle;
8use language::{Buffer, Diff};
9
10pub struct Prettier {
11 _private: (),
12}
13
14pub struct NodeRuntime;
15
16#[derive(Debug)]
17pub struct LocateStart {
18 pub worktree_root_path: Arc<Path>,
19 pub starting_path: Arc<Path>,
20}
21
22impl Prettier {
23 // This was taken from the prettier-vscode extension.
24 pub const CONFIG_FILE_NAMES: &'static [&'static str] = &[
25 ".prettierrc",
26 ".prettierrc.json",
27 ".prettierrc.json5",
28 ".prettierrc.yaml",
29 ".prettierrc.yml",
30 ".prettierrc.toml",
31 ".prettierrc.js",
32 ".prettierrc.cjs",
33 "package.json",
34 "prettier.config.js",
35 "prettier.config.cjs",
36 ".editorconfig",
37 ];
38
39 pub async fn locate(
40 starting_path: Option<LocateStart>,
41 fs: Arc<dyn Fs>,
42 ) -> anyhow::Result<PathBuf> {
43 let paths_to_check = match starting_path {
44 Some(starting_path) => {
45 let worktree_root = starting_path
46 .worktree_root_path
47 .components()
48 .into_iter()
49 .take_while(|path_component| {
50 path_component.as_os_str().to_str() != Some("node_modules")
51 })
52 .collect::<PathBuf>();
53
54 if worktree_root != starting_path.worktree_root_path.as_ref() {
55 vec![worktree_root]
56 } else {
57 let (worktree_root_metadata, start_path_metadata) = if starting_path
58 .starting_path
59 .as_ref()
60 == Path::new("")
61 {
62 let worktree_root_data =
63 fs.metadata(&worktree_root).await.with_context(|| {
64 format!(
65 "FS metadata fetch for worktree root path {worktree_root:?}",
66 )
67 })?;
68 (worktree_root_data.unwrap_or_else(|| {
69 panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}")
70 }), None)
71 } else {
72 let full_starting_path = worktree_root.join(&starting_path.starting_path);
73 let (worktree_root_data, start_path_data) = futures::try_join!(
74 fs.metadata(&worktree_root),
75 fs.metadata(&full_starting_path),
76 )
77 .with_context(|| {
78 format!("FS metadata fetch for starting path {full_starting_path:?}",)
79 })?;
80 (
81 worktree_root_data.unwrap_or_else(|| {
82 panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}")
83 }),
84 start_path_data,
85 )
86 };
87
88 match start_path_metadata {
89 Some(start_path_metadata) => {
90 anyhow::ensure!(worktree_root_metadata.is_dir,
91 "For non-empty start path, worktree root {starting_path:?} should be a directory");
92 anyhow::ensure!(
93 !start_path_metadata.is_dir,
94 "For non-empty start path, it should not be a directory {starting_path:?}"
95 );
96 anyhow::ensure!(
97 !start_path_metadata.is_symlink,
98 "For non-empty start path, it should not be a symlink {starting_path:?}"
99 );
100
101 let file_to_format = starting_path.starting_path.as_ref();
102 let mut paths_to_check = VecDeque::from(vec![worktree_root.clone()]);
103 let mut current_path = worktree_root;
104 for path_component in file_to_format.components().into_iter() {
105 current_path = current_path.join(path_component);
106 paths_to_check.push_front(current_path.clone());
107 if path_component.as_os_str().to_str() == Some("node_modules") {
108 break;
109 }
110 }
111 paths_to_check.pop_front(); // last one is the file itself or node_modules, skip it
112 Vec::from(paths_to_check)
113 }
114 None => {
115 anyhow::ensure!(
116 !worktree_root_metadata.is_dir,
117 "For empty start path, worktree root should not be a directory {starting_path:?}"
118 );
119 anyhow::ensure!(
120 !worktree_root_metadata.is_symlink,
121 "For empty start path, worktree root should not be a symlink {starting_path:?}"
122 );
123 worktree_root
124 .parent()
125 .map(|path| vec![path.to_path_buf()])
126 .unwrap_or_default()
127 }
128 }
129 }
130 }
131 None => Vec::new(),
132 };
133
134 if dbg!(paths_to_check).is_empty() {
135 // TODO kb return the default prettier, how, without state?
136 } else {
137 // TODO kb now check all paths to check for prettier
138 }
139 Ok(PathBuf::new())
140 }
141
142 pub async fn start(prettier_path: &Path, node: Arc<NodeRuntime>) -> anyhow::Result<Self> {
143 todo!()
144 }
145
146 pub async fn format(&self, buffer: &ModelHandle<Buffer>) -> anyhow::Result<Diff> {
147 todo!()
148 }
149
150 pub async fn clear_cache(&self) -> anyhow::Result<()> {
151 todo!()
152 }
153}