11use crate :: case_model:: Style ;
22use std:: path:: Path ;
33
4+ use super :: languages;
5+
46/// Language-specific heuristics for resolving case style ambiguity
57pub struct LanguageHeuristics ;
68
@@ -19,198 +21,40 @@ impl LanguageHeuristics {
1921 let context = preceding_context. trim ( ) ;
2022
2123 match extension {
22- "rb" => Self :: ruby_heuristics ( context, possible_styles) ,
23- "py" => Self :: python_heuristics ( context, possible_styles) ,
24- "js" | "jsx" | "ts" | "tsx" => Self :: javascript_heuristics ( context, possible_styles) ,
25- "go" => Self :: go_heuristics ( context, possible_styles) ,
26- "rs" => Self :: rust_heuristics ( context, possible_styles) ,
27- "java" => Self :: java_heuristics ( context, possible_styles) ,
28- "c" | "cpp" | "cc" | "h" | "hpp" => Self :: c_cpp_heuristics ( context, possible_styles) ,
29- "css" | "scss" | "sass" | "less" => Self :: css_heuristics ( context, possible_styles) ,
30- "html" | "htm" | "xml" => Self :: html_heuristics ( context, possible_styles) ,
31- _ => None ,
32- }
33- }
34-
35- fn ruby_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
36- if context. ends_with ( "class" ) || context. ends_with ( "module" ) {
37- // Classes and modules should be PascalCase
38- if possible_styles. contains ( & Style :: Pascal ) {
39- return Some ( Style :: Pascal ) ;
40- }
41- } else if context. ends_with ( "def" ) {
42- // Methods should be snake_case
43- if possible_styles. contains ( & Style :: Snake ) {
44- return Some ( Style :: Snake ) ;
45- }
46- } else if context. contains ( "CONSTANT" ) || context. contains ( "VERSION" ) {
47- // Constants are SCREAMING_SNAKE
48- if possible_styles. contains ( & Style :: ScreamingSnake ) {
49- return Some ( Style :: ScreamingSnake ) ;
50- }
51- }
52- None
53- }
54-
55- fn python_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
56- if context. ends_with ( "class" ) {
57- // Classes should be PascalCase
58- if possible_styles. contains ( & Style :: Pascal ) {
59- return Some ( Style :: Pascal ) ;
60- }
61- } else if context. ends_with ( "def" ) {
62- // Functions should be snake_case
63- if possible_styles. contains ( & Style :: Snake ) {
64- return Some ( Style :: Snake ) ;
65- }
66- } else if context
67- . chars ( )
68- . all ( |c| c. is_uppercase ( ) || c == '_' || c == '=' )
69- {
70- // Constants (all caps context)
71- if possible_styles. contains ( & Style :: ScreamingSnake ) {
72- return Some ( Style :: ScreamingSnake ) ;
73- }
74- }
75- None
76- }
77-
78- fn javascript_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
79- if context. ends_with ( "class" ) || context. ends_with ( "interface" ) || context. ends_with ( "type" )
80- {
81- // Classes, interfaces, and types should be PascalCase
82- if possible_styles. contains ( & Style :: Pascal ) {
83- return Some ( Style :: Pascal ) ;
84- }
85- } else if context. ends_with ( "function" )
86- || context. ends_with ( "const" )
87- || context. ends_with ( "let" )
88- || context. ends_with ( "var" )
89- {
90- // Functions and variables typically camelCase
91- if possible_styles. contains ( & Style :: Camel ) {
92- return Some ( Style :: Camel ) ;
93- }
94- } else if context. contains ( "export const" )
95- && possible_styles. contains ( & Style :: ScreamingSnake )
96- {
97- // Exported constants might be SCREAMING_SNAKE
98- return Some ( Style :: ScreamingSnake ) ;
99- }
100- None
101- }
102-
103- fn go_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
104- if context. ends_with ( "type" )
105- || context. ends_with ( "struct" )
106- || context. ends_with ( "interface" )
107- {
108- // Types: PascalCase for exported, camelCase for private
109- // Since we don't know if it's exported, prefer PascalCase if available
110- if possible_styles. contains ( & Style :: Pascal ) {
111- return Some ( Style :: Pascal ) ;
112- } else if possible_styles. contains ( & Style :: Camel ) {
113- return Some ( Style :: Camel ) ;
114- }
115- } else if context. ends_with ( "func" ) {
116- // Functions: same as types
117- if possible_styles. contains ( & Style :: Pascal ) {
118- return Some ( Style :: Pascal ) ;
119- } else if possible_styles. contains ( & Style :: Camel ) {
120- return Some ( Style :: Camel ) ;
121- }
122- }
123- None
124- }
125-
126- fn rust_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
127- if context. ends_with ( "struct" )
128- || context. ends_with ( "enum" )
129- || context. ends_with ( "trait" )
130- || context. ends_with ( "impl" )
131- {
132- // Types should be PascalCase
133- if possible_styles. contains ( & Style :: Pascal ) {
134- return Some ( Style :: Pascal ) ;
135- }
136- } else if context. ends_with ( "fn" ) || context. ends_with ( "let" ) || context. ends_with ( "mut" ) {
137- // Functions and variables should be snake_case
138- if possible_styles. contains ( & Style :: Snake ) {
139- return Some ( Style :: Snake ) ;
140- }
141- } else if context. ends_with ( "const" ) || context. ends_with ( "static" ) {
142- // Constants are SCREAMING_SNAKE
143- if possible_styles. contains ( & Style :: ScreamingSnake ) {
144- return Some ( Style :: ScreamingSnake ) ;
145- }
146- }
147- None
148- }
149-
150- fn java_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
151- if context. ends_with ( "class" ) || context. ends_with ( "interface" ) || context. ends_with ( "enum" )
152- {
153- // Classes should be PascalCase
154- if possible_styles. contains ( & Style :: Pascal ) {
155- return Some ( Style :: Pascal ) ;
156- }
157- } else if context. contains ( "private" )
158- || context. contains ( "public" )
159- || context. contains ( "protected" )
160- {
161- // Methods and fields typically camelCase
162- if possible_styles. contains ( & Style :: Camel ) {
163- return Some ( Style :: Camel ) ;
164- }
165- } else if context. ends_with ( "final" ) && context. contains ( "static" ) {
166- // Constants are SCREAMING_SNAKE
167- if possible_styles. contains ( & Style :: ScreamingSnake ) {
168- return Some ( Style :: ScreamingSnake ) ;
169- }
170- }
171- None
172- }
24+ // Programming languages
25+ "rb" | "rake" | "gemspec" => languages:: ruby:: suggest_style ( context, possible_styles) ,
26+ "py" | "pyw" | "pyi" => languages:: python:: suggest_style ( context, possible_styles) ,
27+ "js" | "jsx" | "mjs" | "cjs" | "ts" | "tsx" => {
28+ languages:: javascript:: suggest_style ( context, possible_styles)
29+ } ,
30+ "go" => languages:: go:: suggest_style ( context, possible_styles) ,
31+ "rs" => languages:: rust:: suggest_style ( context, possible_styles) ,
32+ "java" | "kt" | "kts" => languages:: java:: suggest_style ( context, possible_styles) ,
33+ "c" | "cpp" | "cc" | "cxx" | "h" | "hpp" | "hxx" => {
34+ languages:: c_cpp:: suggest_style ( context, possible_styles)
35+ } ,
36+
37+ // Web technologies
38+ "css" | "scss" | "sass" | "less" | "styl" => {
39+ languages:: css:: suggest_style ( context, possible_styles)
40+ } ,
41+ "html" | "htm" | "xml" | "svg" | "vue" => {
42+ languages:: html:: suggest_style ( context, possible_styles)
43+ } ,
44+
45+ // Shell and scripting
46+ "sh" | "bash" | "zsh" | "fish" | "ksh" => {
47+ languages:: shell:: suggest_style ( context, possible_styles)
48+ } ,
49+
50+ // Configuration files
51+ "yml" | "yaml" => languages:: yaml:: suggest_style ( context, possible_styles) ,
52+ "json" | "jsonc" | "json5" | "toml" | "ini" | "cfg" | "conf" | "env" => {
53+ languages:: config:: suggest_style ( context, possible_styles)
54+ } ,
17355
174- fn c_cpp_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
175- if context. ends_with ( "class" ) || context. ends_with ( "struct" ) {
176- // Classes and structs often PascalCase
177- if possible_styles. contains ( & Style :: Pascal ) {
178- return Some ( Style :: Pascal ) ;
179- }
180- } else if context. ends_with ( "#define" ) {
181- // Macros are SCREAMING_SNAKE
182- if possible_styles. contains ( & Style :: ScreamingSnake ) {
183- return Some ( Style :: ScreamingSnake ) ;
184- }
185- } else if context. contains ( "typedef" ) {
186- // Typedefs often use snake_case or PascalCase
187- if possible_styles. contains ( & Style :: Pascal ) {
188- return Some ( Style :: Pascal ) ;
189- } else if possible_styles. contains ( & Style :: Snake ) {
190- return Some ( Style :: Snake ) ;
191- }
192- }
193- None
194- }
195-
196- fn css_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
197- // CSS classes and IDs typically use kebab-case
198- if ( context. ends_with ( '.' ) || context. ends_with ( '#' ) || context. contains ( "class=" ) )
199- && possible_styles. contains ( & Style :: Kebab )
200- {
201- return Some ( Style :: Kebab ) ;
202- }
203- None
204- }
205-
206- fn html_heuristics ( context : & str , possible_styles : & [ Style ] ) -> Option < Style > {
207- // HTML attributes and data attributes use kebab-case
208- if ( context. contains ( "data-" ) || context. contains ( "class=" ) || context. contains ( "id=" ) )
209- && possible_styles. contains ( & Style :: Kebab )
210- {
211- return Some ( Style :: Kebab ) ;
56+ _ => None ,
21257 }
213- None
21458 }
21559}
21660
@@ -249,18 +93,46 @@ mod tests {
24993 #[ test]
25094 fn test_javascript_function_heuristic ( ) {
25195 let path = PathBuf :: from ( "test.js" ) ;
252- let possible_styles = vec ! [ Style :: Camel , Style :: Snake , Style :: Kebab ] ;
96+ let possible_styles = vec ! [ Style :: Camel , Style :: Snake ] ;
25397
25498 let result = LanguageHeuristics :: suggest_style ( & path, "function " , & possible_styles) ;
25599 assert_eq ! ( result, Some ( Style :: Camel ) ) ;
256100 }
257101
258102 #[ test]
259103 fn test_no_matching_style ( ) {
260- let path = PathBuf :: from ( "test.rb " ) ;
261- let possible_styles = vec ! [ Style :: Camel , Style :: Snake ] ; // No PascalCase
104+ let path = PathBuf :: from ( "test.py " ) ;
105+ let possible_styles = vec ! [ Style :: Kebab ] ; // Python doesn't use kebab
262106
263- let result = LanguageHeuristics :: suggest_style ( & path, "class " , & possible_styles) ;
264- assert_eq ! ( result, None ) ; // Can't suggest PascalCase if it's not possible
107+ let result = LanguageHeuristics :: suggest_style ( & path, "def " , & possible_styles) ;
108+ assert_eq ! ( result, None ) ;
109+ }
110+
111+ #[ test]
112+ fn test_unsupported_extension ( ) {
113+ let path = PathBuf :: from ( "test.xyz" ) ;
114+ let possible_styles = vec ! [ Style :: Snake , Style :: Camel ] ;
115+
116+ let result = LanguageHeuristics :: suggest_style ( & path, "function " , & possible_styles) ;
117+ assert_eq ! ( result, None ) ;
118+ }
119+
120+ #[ test]
121+ fn test_shell_export_heuristic ( ) {
122+ let path = PathBuf :: from ( "script.sh" ) ;
123+ let possible_styles = vec ! [ Style :: ScreamingSnake , Style :: Snake ] ;
124+
125+ // Note: "export " gets trimmed to "export" in suggest_style
126+ let result = LanguageHeuristics :: suggest_style ( & path, "export " , & possible_styles) ;
127+ assert_eq ! ( result, Some ( Style :: ScreamingSnake ) ) ;
128+ }
129+
130+ #[ test]
131+ fn test_yaml_key_heuristic ( ) {
132+ let path = PathBuf :: from ( "config.yml" ) ;
133+ let possible_styles = vec ! [ Style :: Snake , Style :: Camel ] ;
134+
135+ let result = LanguageHeuristics :: suggest_style ( & path, "key:" , & possible_styles) ;
136+ assert_eq ! ( result, Some ( Style :: Snake ) ) ;
265137 }
266138}
0 commit comments