sloc_languages/style/
common.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
12#[serde(rename_all = "snake_case")]
13pub enum IndentStyle {
14 Tabs,
15 Spaces2,
16 Spaces4,
17 Spaces8,
18 Mixed,
19 #[default]
20 Unknown,
21}
22
23impl IndentStyle {
24 pub fn display(self) -> &'static str {
25 match self {
26 Self::Tabs => "Tabs",
27 Self::Spaces2 => "2-Space",
28 Self::Spaces4 => "4-Space",
29 Self::Spaces8 => "8-Space",
30 Self::Mixed => "Mixed",
31 Self::Unknown => "\u{2014}",
32 }
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct StyleSignal {
41 pub name: String,
43 pub value: String,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct StyleGuideScore {
50 pub name: String,
51 pub description: String,
53 pub score_pct: u8,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, Default)]
59pub struct StyleAnalysis {
60 pub language_family: String,
62
63 pub indent_style: IndentStyle,
65 pub tab_indented_lines: u32,
66 pub space2_indented_lines: u32,
67 pub space4_indented_lines: u32,
68 pub lines_over_80: u32,
69 pub lines_over_100: u32,
70 pub lines_over_120: u32,
71 pub max_line_length: u32,
72 pub total_lines: u32,
73
74 pub signals: Vec<StyleSignal>,
76
77 pub guide_scores: Vec<StyleGuideScore>,
79 pub dominant_guide: String,
80 pub dominant_score_pct: u8,
81}
82
83pub fn scan_indent(line: &str, tabs: &mut u32, sp2: &mut u32, sp4: &mut u32) {
87 let first = match line.chars().next() {
88 Some(c) => c,
89 None => return,
90 };
91 if first == '\t' {
92 *tabs += 1;
93 return;
94 }
95 if first != ' ' {
96 return;
97 }
98 let leading = line.bytes().take_while(|&b| b == b' ').count();
99 if leading == 0 {
100 return;
101 }
102 if leading % 4 == 0 {
103 *sp4 += 1;
104 } else if leading % 2 == 0 {
105 *sp2 += 1;
106 }
107}
108
109pub fn classify_indent(tabs: u32, sp2: u32, sp4: u32) -> IndentStyle {
111 let total = tabs + sp2 + sp4;
112 if total == 0 {
113 return IndentStyle::Unknown;
114 }
115 let tab_pct = tabs as f32 / total as f32;
116 let s2_pct = sp2 as f32 / total as f32;
117 let s4_pct = sp4 as f32 / total as f32;
118 if tab_pct >= 0.60 {
119 return IndentStyle::Tabs;
120 }
121 if s4_pct >= 0.60 {
122 return IndentStyle::Spaces4;
123 }
124 if s2_pct >= 0.60 {
125 return IndentStyle::Spaces2;
126 }
127 if sp4 > sp2 * 2 && sp4 > tabs {
128 return IndentStyle::Spaces4;
129 }
130 if sp2 > sp4 && sp2 > tabs {
131 return IndentStyle::Spaces2;
132 }
133 IndentStyle::Mixed
134}
135
136pub fn weighted_score(features: &[(f32, f32)]) -> u8 {
140 let s: f32 = features.iter().map(|(w, v)| w * v).sum();
141 (s * 100.0).round().clamp(0.0, 100.0) as u8
142}
143
144pub fn score_indent_2(s: IndentStyle) -> f32 {
145 match s {
146 IndentStyle::Spaces2 => 1.0,
147 IndentStyle::Mixed => 0.35,
148 _ => 0.05,
149 }
150}
151
152pub fn score_indent_4(s: IndentStyle) -> f32 {
153 match s {
154 IndentStyle::Spaces4 => 1.0,
155 IndentStyle::Mixed => 0.35,
156 _ => 0.05,
157 }
158}
159
160pub fn score_indent_tabs(s: IndentStyle) -> f32 {
161 match s {
162 IndentStyle::Tabs => 1.0,
163 IndentStyle::Mixed => 0.20,
164 _ => 0.05,
165 }
166}
167
168pub fn score_line80(over: u32, total: u32) -> f32 {
170 if total == 0 {
171 return 1.0;
172 }
173 let p = over as f32 / total as f32;
174 if p < 0.02 {
175 1.00
176 } else if p < 0.08 {
177 0.75
178 } else if p < 0.20 {
179 0.45
180 } else {
181 0.10
182 }
183}
184
185pub fn score_line88(over88: u32, total: u32) -> f32 {
187 score_line_n(over88, total)
188}
189
190pub fn score_line100(over100: u32, total: u32) -> f32 {
192 score_line_n(over100, total)
193}
194
195pub fn score_line120(over120: u32, total: u32) -> f32 {
197 score_line_n(over120, total)
198}
199
200fn score_line_n(over: u32, total: u32) -> f32 {
201 if total == 0 {
202 return 1.0;
203 }
204 let p = over as f32 / total as f32;
205 if p < 0.03 {
206 1.00
207 } else if p < 0.10 {
208 0.75
209 } else if p < 0.25 {
210 0.45
211 } else {
212 0.10
213 }
214}
215
216pub fn count_over(lines: &[&str], limit: usize) -> u32 {
218 lines.iter().filter(|l| l.len() > limit).count() as u32
219}