prettier.rs

  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}