@@ -21,13 +21,14 @@ def get_tab_spaces(code: str) -> int:
2121 non_zero_indents = [i for i in indents if i > 0 ]
2222 return min (non_zero_indents ) if non_zero_indents else 0
2323
24- def change_tab_size (self , code : str , new_tab_size : int , remove_empty_lines : bool = False ) -> str :
24+ @staticmethod
25+ def change_tab_size (code : str , new_tab_size : int , remove_empty_lines : bool = False ) -> str :
2526 """Replaces all tabs with `new_tab_size` spaces.\n
2627 ----------------------------------------------------------------------------------
2728 If `remove_empty_lines` is `True`, empty lines will be removed in the process."""
2829 code_lines = String .get_lines (code , remove_empty_lines = True )
2930 lines = code_lines if remove_empty_lines else String .get_lines (code )
30- tab_spaces = self .get_tab_spaces (code )
31+ tab_spaces = Code .get_tab_spaces (code )
3132 if (tab_spaces == new_tab_size ) or tab_spaces == 0 :
3233 if remove_empty_lines :
3334 return "\n " .join (code_lines )
@@ -54,46 +55,61 @@ def get_func_calls(code: str) -> list:
5455 @staticmethod
5556 def is_js (code : str , funcs : list = ["__" , "$t" , "$lang" ]) -> bool :
5657 """Will check if the code is very likely to be JavaScript."""
57- funcs = "|" .join (funcs )
58- js_pattern = _rx .compile (
59- Regex .outside_strings (
60- r"""^(?:
61- (\$[\w_]+)\s* # JQUERY-STYLE VARIABLES
62- |(\$[\w_]+\s*\() # JQUERY-STYLE FUNCTION CALLS
63- |((""" + funcs + r")" + Regex .brackets ("()" ) + r"""\s*) # PREDEFINED FUNCTION CALLS
64- |(\bfunction\s*\() # FUNCTION DECLARATIONS
65- |(\b(var|let|const)\s+[\w_]+\s*=) # VARIABLE DECLARATIONS
66- |(\b(if|for|while|switch)\s*\() # CONTROL STRUCTURES
67- |(\b(return|throw)\s+) # RETURN OR THROW STATEMENTS
68- |(\bnew\s+[\w_]+\() # OBJECT INSTANTIATION
69- |(\b[\w_]+\s*=>\s*{) # ARROW FUNCTIONS
70- |(\b(true|false|null|undefined)\b) # JAVASCRIPT LITERALS
71- |(\b(document|window|console)\.) # BROWSER OBJECTS
72- |(\b[\w_]+\.(forEach|map|filter|reduce)\() # ARRAY METHODS
73- |(/[^/\n\r]*?/[gimsuy]*) # REGULAR EXPRESSIONS
74- |(===|!==|\+\+|--|\|\||&&) # JAVASCRIPT-SPECIFIC OPERATORS
75- |(\bclass\s+[\w_]+) # CLASS DECLARATIONS
76- |(\bimport\s+.*?from\s+) # IMPORT STATEMENTS
77- |(\bexport\s+(default\s+)?) # EXPORT STATEMENTS
78- |(\basync\s+function) # ASYNC FUNCTIONS
79- |(\bawait\s+) # AWAIT KEYWORD
80- |(\btry\s*{) # TRY-CATCH BLOCKS
81- |(\bcatch\s*\()
82- |(\bfinally\s*{)
83- |(\byield\s+) # GENERATOR FUNCTIONS
84- |(\[.*?\]\s*=) # DESTRUCTURING ASSIGNMENT
85- |(\.\.\.) # SPREAD OPERATOR
86- |(==|!=|>=|<=|>|<) # COMPARISON OPERATORS
87- |(\+=|-=|\*=|/=|%=|\*\*=) # COMPOUND ASSIGNMENT OPERATORS
88- |(\+|-|\*|/|%|\*\*) # ARITHMETIC OPERATORS
89- |(&|\||\^|~|<<|>>|>>>) # BITWISE OPERATORS
90- |(\?|:) # TERNARY OPERATOR
91- |(\bin\b) # IN OPERATOR
92- |(\binstanceof\b) # INSTANCEOF OPERATOR
93- |(\bdelete\b) # DELETE OPERATOR
94- |(\btypeof\b) # TYPEOF OPERATOR
95- |(\bvoid\b) # VOID OPERATOR
96- )[\s\S]*$"""
97- ), _rx .VERBOSE | _rx .IGNORECASE
98- )
99- return bool (js_pattern .fullmatch (code ))
58+ if not code or len (code .strip ()) < 3 :
59+ return False
60+ for func in funcs :
61+ if _rx .match (r"^[\s\n]*" + _rx .escape (func ) + r"\([^\)]*\)[\s\n]*$" , code ):
62+ return True
63+ direct_js_patterns = [
64+ r"^[\s\n]*\$\(['\"][^'\"]+['\"]\)\.[\w]+\([^\)]*\);?[\s\n]*$" , # jQuery calls
65+ r"^[\s\n]*\$\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$" , # $.ajax(), etc.
66+ r"^[\s\n]*\(\s*function\s*\(\)\s*\{.*\}\s*\)\(\);?[\s\n]*$" , # IIFE
67+ r"^[\s\n]*document\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$" , # document.getElementById()
68+ r"^[\s\n]*window\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$" , # window.alert()
69+ r"^[\s\n]*console\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$" , # console.log()
70+ ]
71+ for pattern in direct_js_patterns :
72+ if _rx .match (pattern , code ):
73+ return True
74+ arrow_function_patterns = [
75+ r"^[\s\n]*\b[\w_]+\s*=\s*\([^\)]*\)\s*=>\s*[^;{]*[;]?[\s\n]*$" , # const x = (y) => y*2;
76+ r"^[\s\n]*\b[\w_]+\s*=\s*[\w_]+\s*=>\s*[^;{]*[;]?[\s\n]*$" , # const x = y => y*2;
77+ r"^[\s\n]*\(\s*[\w_,\s]+\s*\)\s*=>\s*[^;{]*[;]?[\s\n]*$" , # (x) => x*2
78+ r"^[\s\n]*[\w_]+\s*=>\s*[^;{]*[;]?[\s\n]*$" , # x => x*2
79+ ]
80+ for pattern in arrow_function_patterns :
81+ if _rx .match (pattern , code ):
82+ return True
83+ funcs_pattern = r"(" + "|" .join (_rx .escape (f ) for f in funcs ) + r")" + Regex .brackets ("()" )
84+ js_indicators = [(r"\b(var|let|const)\s+[\w_$]+" , 2 ), # JS variable declarations
85+ (r"\$[\w_$]+\s*=" , 2 ), # jQuery-style variables
86+ (r"\$[\w_$]+\s*\(" , 2 ), # jQuery function calls
87+ (r"\bfunction\s*[\w_$]*\s*\(" , 2 ), # Function declarations
88+ (r"[\w_$]+\s*=\s*function\s*\(" , 2 ), # Function assignments
89+ (r"\b[\w_$]+\s*=>\s*[\{\(]" , 2 ), # Arrow functions
90+ (r"\(function\s*\(\)\s*\{" , 2 ), # IIFE pattern
91+ (funcs_pattern , 2 ), # Custom predefined functions
92+ (r"\b(true|false|null|undefined)\b" , 1 ), # JS literals
93+ (r"===|!==|\+\+|--|\|\||&&" , 1.5 ), # JS-specific operators
94+ (r"\bnew\s+[\w_$]+\s*\(" , 1.5 ), # Object instantiation with new
95+ (r"\b(document|window|console|Math|Array|Object|String|Number)\." , 2 ), # JS objects
96+ (r"\basync\s+function|\bawait\b" , 2 ), # Async/await
97+ (r"\b(if|for|while|switch)\s*\([^)]*\)\s*\{" , 1 ), # Control structures with braces
98+ (r"\btry\s*\{[^}]*\}\s*catch\s*\(" , 1.5 ), # Try-catch
99+ (r";[\s\n]*$" , 0.5 ), # Semicolon line endings
100+ ]
101+ js_score = 0
102+ line_endings = [line .strip () for line in code .splitlines () if line .strip ()]
103+ semicolon_endings = sum (1 for line in line_endings if line .endswith (';' ))
104+ if semicolon_endings >= 1 :
105+ js_score += min (semicolon_endings , 2 )
106+ opening_braces = code .count ('{' )
107+ closing_braces = code .count ('}' )
108+ if opening_braces > 0 and opening_braces == closing_braces :
109+ js_score += 1
110+ for pattern , score in js_indicators :
111+ regex = _rx .compile (pattern , _rx .IGNORECASE )
112+ matches = regex .findall (code )
113+ if matches :
114+ js_score += len (matches ) * score
115+ return js_score >= 2
0 commit comments