1use std::path::Path;
39
40#[derive(Debug, Clone, PartialEq)]
41pub enum CommentStyle {
42 Line(String),
43 Block(String, String),
44 BlockTriple(String),
46}
47
48impl CommentStyle {
49 pub fn prefix(&self) -> &str {
50 match self {
51 CommentStyle::Line(p) => p,
52 CommentStyle::Block(o, _) => o,
53 CommentStyle::BlockTriple(d) => d,
54 }
55 }
56}
57
58pub fn comment_style_for(path: &Path) -> Option<CommentStyle> {
59 let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
60 let fname = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
61
62 match fname {
64 "Makefile" | "makefile" | "Dockerfile" | "Containerfile" => {
65 return Some(CommentStyle::Line("#".to_string()));
66 }
67 "CMakeLists.txt" => return Some(CommentStyle::Line("#".to_string())),
68 "Rakefile" | "Gemfile" => return Some(CommentStyle::Line("#".to_string())),
69 _ => {}
70 }
71
72 match ext {
74 "rs" | "go" | "c" | "h" | "cpp" | "hpp" | "cc" | "cxx" | "hh" | "java" | "js" | "ts"
76 | "jsx" | "tsx" | "kt" | "kts" | "swift" | "zig" | "dart" | "php" | "m" | "mm" | "cs"
77 | "scala" | "groovy" | "vala" | "v" | "nu" | "svelte" | "vue" => {
78 Some(CommentStyle::Line("//".to_string()))
79 }
80
81 "py" | "rb" | "pl" | "pm" | "sh" | "bash" | "zsh" | "fish" | "yaml" | "yml" | "toml"
83 | "r" | "R" | "rake" | "gemspec" | "erb" | "el" | "ex" | "exs" | "jl" | "coffee"
84 | "csh" | "awk" | "tcl" | "mk" | "ninja" | "bzl" | "BUILD" | "WORKSPACE" | "dhall"
85 | "nix" | "conf" | "cfg" | "ini" | "desktop" | "service" | "timer" | "diff" | "patch"
86 | "ziggy" | "ziggy-schema" => Some(CommentStyle::Line("#".to_string())),
87
88 "sql" | "hs" | "lhs" | "lua" | "ada" | "adb" | "ads" | "sqlite" | "sqlite3" | "vhd"
90 | "vhdl" => Some(CommentStyle::Line("--".to_string())),
91
92 "tex" | "sty" | "cls" | "ltx" | "bib" | "matlab" | "maxima" | "prolog" => {
94 Some(CommentStyle::Line("%".to_string()))
95 }
96
97 "lisp" | "lsp" | "clj" | "cljs" | "cljc" | "edn" | "scm" | "ss" => {
99 Some(CommentStyle::Line(";".to_string()))
100 }
101
102 "html" | "htm" | "xhtml" | "xml" | "xsl" | "xslt" | "xsd" | "svg" | "rmd" | "mdown"
104 | "mkdn" | "mdx" => Some(CommentStyle::Block("<!--".into(), "-->".into())),
105
106 "css" | "scss" | "sass" | "less" | "graphql" | "gql" | "proto" | "flatbuffers" | "fbs"
108 | "solidity" | "sol" => Some(CommentStyle::Block("/*".to_string(), "*/".to_string())),
109
110 _ => None,
111 }
112}
113
114pub fn format_as_comment(lines: &[String], style: &CommentStyle) -> String {
115 match style {
116 CommentStyle::Line(prefix) => lines
117 .iter()
118 .map(|l| {
119 if l.trim().is_empty() {
120 prefix.clone()
121 } else {
122 format!("{} {}", prefix, l.trim())
123 }
124 })
125 .collect::<Vec<_>>()
126 .join("\n"),
127 CommentStyle::Block(open, close) => {
128 let mut result = open.clone();
129 result.push('\n');
130 for line in lines {
131 if line.trim().is_empty() {
132 result.push_str(open);
133 result.push('\n');
134 } else {
135 result.push_str(&format!("{} {}", open, line.trim()));
136 result.push('\n');
137 }
138 }
139 result.push_str(close);
140 result.push('\n');
141 result
142 }
143 CommentStyle::BlockTriple(delim) => {
144 let mut result = delim.clone();
145 result.push('\n');
146 for line in lines {
147 result.push_str(line.trim());
148 result.push('\n');
149 }
150 result.push_str(delim);
151 result.push('\n');
152 result
153 }
154 }
155}