Skip to content

Commit 9ebc994

Browse files
committed
x
1 parent 0338999 commit 9ebc994

14 files changed

Lines changed: 566 additions & 179 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
<code>_ignore_linebreaks: *bool* = False</code> whether to include linebreaks in the removal positions or not
2424
* renamed param `correct_path` in `Path.extend()` and param `correct_paths` in `File.extend_or_make_path()` to `use_closest_match`, since this name describes its functionality better
2525
* moved the method `extend_or_make_path()` from the `xx_file` module to the `xx_path` module and renamed it to `extend_or_make()`
26+
* added a new param to method `Color.luminance()` and to the `.grayscale()` method of all color types:
27+
- <code>method: *str* = "wcag2"</code> the luminance calculation method to use
2628

2729
## 18.03.2025 `v1.6.8`
2830
* made it possible to escape formatting codes by putting a slash (`/` *or* `\\`) at the beginning inside the brackets (*e.g.* `[/red]`)

pyproject.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,15 @@ minversion = "7.0"
151151
addopts = "-ra -q"
152152
pythonpath = ["src"]
153153
testpaths = [
154-
"tests/test_console_info.py",
155-
"tests/test_color_types.py",
154+
"tests/test_code.py",
156155
"tests/test_color.py",
156+
"tests/test_color_types.py",
157+
"tests/test_console.py",
157158
"tests/test_data.py",
158-
"tests/test_env_vars.py",
159+
"tests/test_env_path.py",
160+
"tests/test_file.py",
159161
"tests/test_format_codes.py",
162+
"tests/test_json.py",
163+
"tests/test_path.py",
164+
"tests/test_string.py",
160165
]

src/xulbux/xx_code.py

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)