SeleniumBase/seleniumbase/console_scripts/sb_print.py

644 lines
30 KiB
Python

"""
Prints the code/text of any file with syntax-highlighting
Usage:
seleniumbase print [FILE] [OPTIONS]
OR: sbase print [FILE] [OPTIONS]
Options:
-n (Add line Numbers to the rows)
Output:
Prints the code/text of any file
with syntax-highlighting.
"""
import colorama
import os
import sys
def invalid_run_command(msg=None):
exp = " ** print **\n\n"
exp += " Usage:\n"
exp += " seleniumbase print [FILE] [OPTIONS]\n"
exp += " OR: sbase print [FILE] [OPTIONS]\n"
exp += " Options:\n"
exp += " -n (Add line Numbers to the rows)\n"
exp += " Output:\n"
exp += " Prints the code/text of any file\n"
exp += " with syntax-highlighting.\n"
if not msg:
raise Exception("INVALID RUN COMMAND!\n\n%s" % exp)
else:
raise Exception("INVALID RUN COMMAND!\n%s\n\n%s" % (msg, exp))
def sc_ranges():
# Get the ranges of special double-width characters.
special_char_ranges = [
{"from": ord("\u4e00"), "to": ord("\u9FFF")},
{"from": ord("\u3040"), "to": ord("\u30ff")},
{"from": ord("\uac00"), "to": ord("\ud7a3")},
{"from": ord("\uff01"), "to": ord("\uff60")},
]
return special_char_ranges
def is_char_wide(char):
# Returns True if the special character is Chinese, Japanese, or Korean.
sc = any(
[range["from"] <= ord(char) <= range["to"] for range in sc_ranges()]
)
return sc
def get_width(line):
# Return the true width of the line. Not the same as line length.
# Chinese/Japanese/Korean characters take up two spaces of width.
line_length = len(line)
for char in line:
if is_char_wide(char):
line_length += 1
return line_length
def main():
if (
"win32" in sys.platform
and hasattr(colorama, "just_fix_windows_console")
):
colorama.just_fix_windows_console()
else:
colorama.init(autoreset=True)
c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX
c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA
cr = colorama.Style.RESET_ALL
line_numbers = False
word_wrap = True # Always use word wrap now
help_me = False
invalid_cmd = None
is_python_file = False
code_lang = None
command_args = sys.argv[2:]
file_to_print = command_args[0]
if file_to_print.lower().endswith(".py"):
is_python_file = True
code_lang = "python"
elif file_to_print.lower().endswith(".js"):
code_lang = "javascript"
elif file_to_print.lower().endswith(".md"):
code_lang = "markdown"
elif file_to_print.lower().endswith(".html"):
code_lang = "html"
elif file_to_print.lower().endswith(".css"):
code_lang = "css"
elif file_to_print.lower().endswith(".go"):
code_lang = "go"
elif file_to_print.lower().endswith(".java"):
code_lang = "java"
elif file_to_print.lower().endswith(".feature"):
code_lang = "gherkin"
elif file_to_print.lower().endswith(".txt"):
code_lang = "javascript"
elif file_to_print.lower().endswith(".yml"):
code_lang = "javascript"
elif file_to_print.lower().endswith(".in"):
code_lang = "javascript"
elif "." not in file_to_print:
code_lang = "markdown"
else:
code_lang = file_to_print.split(".")[-1].lower()
if len(command_args) >= 2:
options = command_args[1:]
for option in options:
option = option.lower()
if option == "-n":
line_numbers = True
elif option == "-w":
word_wrap = True
else:
invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option
invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ")
invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<")
invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr)
invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr)
help_me = True
break
if help_me:
invalid_run_command(invalid_cmd)
all_code = None
with open(
file_to_print, "r+", encoding="utf-8", errors="ignore"
) as f:
all_code = f.read()
all_code = all_code.replace("\t", " ")
code_lines = all_code.split("\n")
console_width = None # width of console output when running script
used_width = None # code_width and few spaces on right for padding
magic_syntax = None # the syntax generated by rich.syntax.Syntax()
try:
console_width = os.get_terminal_size().columns
if console_width:
console_width = int(console_width)
except Exception:
console_width = None
from seleniumbase.console_scripts import rich_helper
the_code = "\n".join(code_lines)
code_width = 1
w = 0 # line number whitespace
if line_numbers:
w = 4
num_lines = len(code_lines)
if num_lines >= 10:
w = 5
if num_lines >= 100:
w = 6
if num_lines >= 1000:
w = 7
if num_lines >= 10000:
w = 8
if is_python_file:
new_sb_lines = []
for line in code_lines:
if line.endswith(" # noqa") and line.count(" # noqa") == 1:
line = line.replace(" # noqa", "")
line_length2 = len(line) # Normal Python string length used
line_length = get_width(line) # Special characters count 2X
if line_length > code_width:
code_width = line_length
if console_width:
# If line is larger than console_width, try to optimize it.
# Smart Python word wrap to be used with valid indentation.
if line_length + w > console_width: # 5 is line number ws
if line.strip().startswith("#"):
new_sb_lines.append(line)
continue
elif (
line.count(" # ") == 1
and get_width(line.split(" # ")[0]) + w
<= console_width
):
# Line is short enough once comment is removed
line = line.split(" # ")[0]
new_sb_lines.append(line)
continue
elif (
line.count(" # ") == 1
and get_width(line.split(" # ")[0]) + w
<= console_width
):
# L-Length good if removing bad flake8 comment
line = line.split(" # ")[0]
new_sb_lines.append(line)
continue
if line.startswith("from") and " import " in line:
line1 = line.split(" import ")[0] + " \\"
line2 = " import " + line.split(" import ")[1]
new_sb_lines.append(line1)
new_sb_lines.append(line2)
continue
if (
line.count("(") >= 1
and line.count("(") == line.count(")")
):
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
first_paren = line.find("(")
line1 = line[:first_paren + 1]
line2 = new_ws + line[first_paren + 1:]
if ("):") not in line2:
new_sb_lines.append(line1)
if get_width(line2) + w > console_width:
if line2.count('", "') == 1:
line2a = line2.split('", "')[0] + '",'
line2b = (
new_ws
+ '"'
+ (line2.split('", "')[1])
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
elif line2.count("', '") == 1:
line2a = line2.split("', '")[0] + "',"
line2b = (
new_ws
+ "'"
+ (line2.split("', '")[1])
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
elif line2.count("://") == 1 and (
line2.count('")') == 1
):
line2a = line2.split("://")[0] + '://"'
line2b = (
new_ws
+ '"'
+ (line2.split("://")[1])
)
new_sb_lines.append(line2a)
if get_width(line2b) + w > (
console_width
):
if line2b.count("/") > 0:
slash_one = line2b.find("/")
slash_one_p1 = slash_one + 1
line2b1 = (
line2b[: slash_one + 1]
+ '"'
)
line2b2 = (
new_ws
+ '"'
+ (line2b[slash_one_p1:])
)
new_sb_lines.append(line2b1)
if line2b2.count(") # ") == 1:
line2b2 = (
line2b2.split(") # ")[
0
]
+ ")"
)
new_sb_lines.append(line2b2)
continue
new_sb_lines.append(line2b)
continue
elif line2.count("://") == 1 and (
line2.count("')") == 1
):
line2a = line2.split("://")[0] + "://'"
line2b = (
new_ws
+ "'"
+ (line2.split("://")[1])
)
new_sb_lines.append(line2a)
if get_width(line2b) + w > (
console_width
):
if line2b.count("/") > 0:
slash_one = line2b.find("/")
slash_one_p1 = slash_one + 1
line2b1 = (
line2b[: slash_one + 1]
+ "'"
)
line2b2 = (
new_ws
+ "'"
+ (line2b[slash_one_p1:])
)
new_sb_lines.append(line2b1)
if line2b2.count(") # ") == 1:
line2b2 = (
line2b2.split(") # ")[
0
]
+ ")"
)
new_sb_lines.append(line2b2)
continue
new_sb_lines.append(line2b)
continue
elif line2.count(", ") == 1:
line2a = line2.split(", ")[0] + ","
line2b = new_ws + line2.split(", ")[1]
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
elif line2.count('="') == 1 and (
line2.lstrip().startswith("'")
):
line2a = line2.split('="')[0] + "='"
line2b = (
new_ws
+ "'\""
+ (line2.split('="')[1])
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
elif line2.count("='") == 1 and (
line2.lstrip().startswith('"')
):
line2a = line2.split("='")[0] + '="'
line2b = (
new_ws
+ "\"'"
+ (line2.split("='")[1])
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
new_sb_lines.append(line2)
elif get_width(line2) + 4 + w <= console_width:
line2 = " " + line2
new_sb_lines.append(line1)
new_sb_lines.append(line2)
else:
new_sb_lines.append(line)
continue
if line.count('("') == 1:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split('("')[0] + "("
line2 = new_ws + '"' + line.split('("')[1]
if ("):") not in line2:
new_sb_lines.append(line1)
if get_width(line2) + w > console_width:
if line2.count('" in self.') == 1:
line2a = (
line2.split('" in self.')[0]
+ '" in'
)
line2b = (
new_ws
+ "self."
+ (line2.split('" in self.')[1])
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
new_sb_lines.append(line2)
elif get_width(line2) + 4 + w <= console_width:
line2 = " " + line2
new_sb_lines.append(line1)
new_sb_lines.append(line2)
else:
new_sb_lines.append(line)
continue
if line.count("('") == 1:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split("('")[0] + "("
line2 = new_ws + "'" + line.split("('")[1]
if ("):") not in line2:
new_sb_lines.append(line1)
if get_width(line2) + w > console_width:
if line2.count("' in self.") == 1:
line2a = (
line2.split("' in self.")[0]
+ "' in"
)
line2b = (
new_ws
+ "self."
+ (line2.split("' in self.")[1])
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
new_sb_lines.append(line2)
elif get_width(line2) + 4 + w <= console_width:
line2 = " " + line2
new_sb_lines.append(line1)
new_sb_lines.append(line2)
else:
new_sb_lines.append(line)
continue
if line.count('= "') == 1 and line.count("://") == 1:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split("://")[0] + '://" \\'
line2 = new_ws + '"' + line.split("://")[1]
new_sb_lines.append(line1)
if get_width(line2) + w > console_width:
if line2.count("/") > 0:
slash_one = line2.find("/")
slash_one_p1 = slash_one + 1
line2a = line2[: slash_one + 1] + '" \\'
line2b = (
new_ws + '"' + line2[slash_one_p1:]
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
new_sb_lines.append(line2)
continue
if line.count("= '") == 1 and line.count("://") == 1:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split("://")[0] + "://' \\"
line2 = new_ws + "'" + line.split("://")[1]
new_sb_lines.append(line1)
if get_width(line2) + w > console_width:
if line2.count("/") > 0:
slash_one = line2.find("/")
slash_one_p1 = slash_one + 1
line2a = line2[: slash_one + 1] + "' \\"
line2b = (
new_ws + "'" + line2[slash_one_p1:]
)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
new_sb_lines.append(line2)
continue
if line.count("(self.") == 1 and ("):") not in line:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split("(self.")[0] + "("
line2 = new_ws + "self." + line.split("(self.")[1]
if get_width(line1) + w <= console_width:
new_sb_lines.append(line1)
new_sb_lines.append(line2)
continue
if line.count(" == ") == 1 and not (
line.endswith(":") or (": #") in line
):
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split(" == ")[0] + " == ("
line2 = new_ws + line.split(" == ")[1] + ")"
if get_width(line1) + w <= console_width and (
get_width(line2) + w <= console_width
):
new_sb_lines.append(line1)
new_sb_lines.append(line2)
continue
if line.count(" == ") == 1 and line.endswith(":"):
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split(" == ")[0] + " == ("
line2 = new_ws + line.split(" == ")[1][:-1] + "):"
if get_width(line1) + w <= console_width and (
get_width(line2) + w <= console_width
):
new_sb_lines.append(line1)
new_sb_lines.append(line2)
continue
if (
line.count(" == ") == 1
and (line.count(": #") == 1)
and (line.find(" == ") < line.find(": #"))
):
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
comments = " #" + line.split(": #")[1]
line0 = line.split(": #")[0] + ":"
line1 = line0.split(" == ")[0] + " == ("
line2 = new_ws + line0.split(" == ")[1][:-1] + "):"
if get_width(line1) + w <= console_width and (
get_width(line2) + w <= console_width
):
new_sb_lines.append(line1)
if (
get_width(line2 + comments) + w
<= console_width
):
new_sb_lines.append(line2 + comments)
else:
new_sb_lines.append(line2)
continue
if line.count(" % ") == 1 and ("):") not in line:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split(" % ")[0] + " \\"
line2 = new_ws + "% " + line.split(" % ")[1]
if get_width(line1) + w <= console_width:
new_sb_lines.append(line1)
new_sb_lines.append(line2)
continue
if line.count(" = ") == 1 and (" # ") not in line:
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
line1 = line.split(" = ")[0] + " = ("
line2 = new_ws + line.split(" = ")[1] + ")"
if get_width(line1) + w <= console_width and (
get_width(line2) + w <= console_width
):
new_sb_lines.append(line1)
new_sb_lines.append(line2)
continue
elif get_width(line1) + w <= console_width:
if line2.count(" % ") == 1 and not (
line2.endswith(":")
):
whitespace = line_length2 - len(
line2.lstrip()
)
line2a = line2.split(" % ")[0] + " \\"
line2b = (
new_ws + "% " + line2.split(" % ")[1]
)
if get_width(line2a) + w <= console_width:
if (
get_width(line2b) + w
<= console_width
):
new_sb_lines.append(line1)
new_sb_lines.append(line2a)
new_sb_lines.append(line2b)
continue
if (
line.count(" = ") == 1
and (line.count(" # ") == 1)
and (line.find(" = ") < line.find(" # "))
):
whitespace = line_length2 - len(line.lstrip())
new_ws = line[0:whitespace] + " "
comments = " # " + line.split(" # ")[1]
line0 = line.split(" # ")[0]
line1 = line0.split(" = ")[0] + " = ("
line2 = new_ws + line0.split(" = ")[1] + ")"
if get_width(line1) + w <= console_width and (
get_width(line2) + w <= console_width
):
new_sb_lines.append(line1)
if (
get_width(line2 + comments) + w
<= console_width
):
new_sb_lines.append(line2 + comments)
else:
new_sb_lines.append(line2)
continue
new_sb_lines.append(line)
if new_sb_lines:
code_lines = new_sb_lines
the_code = "\n".join(code_lines)
if code_lang != "python":
for line in code_lines:
line_length = get_width(line)
if line_length > code_width:
code_width = line_length
extra_r_spaces = 2
if console_width and (code_width + extra_r_spaces < console_width):
used_width = code_width + extra_r_spaces
the_code = rich_helper.fix_emoji_spacing(the_code)
the_theme = "monokai"
if file_to_print.split(os.sep)[-1].startswith("."):
the_theme = "tango"
magic_syntax = rich_helper.process_syntax(
the_code,
code_lang,
theme=the_theme,
line_numbers=line_numbers,
code_width=used_width,
word_wrap=word_wrap,
)
# ----------------------------------------
dash_length = 62 # May change
if used_width and used_width + w < console_width:
dash_length = used_width + w
elif console_width:
dash_length = console_width
dashes = "-" * dash_length
print(dashes)
print_success = False
if code_lang == "markdown" and not line_numbers:
all_code = rich_helper.fix_emoji_spacing(all_code)
if "<b>*" not in all_code and "*<b>" not in all_code:
if "</b>*" not in all_code and "*</b>" not in all_code:
all_code = all_code.replace("<b>", "**")
all_code = all_code.replace("</b>", "**")
if "<code>`" not in all_code and "`<code>" not in all_code:
if "</code>`" not in all_code and "`</code>" not in all_code:
all_code = all_code.replace('<code translate="no">', "``")
all_code = all_code.replace("<code>", "``")
all_code = all_code.replace("</code>", "``")
# Display ALL <h> tags as an <h1> because the font size is fixed
all_code = all_code.replace("\n<h1>", "\n# ").replace("</h1>", "")
all_code = all_code.replace("\n<h2>", "\n# ").replace("</h2>", "")
all_code = all_code.replace("\n<h3>", "\n# ").replace("</h3>", "")
all_code = all_code.replace("\n<h4>", "\n# ").replace("</h4>", "")
all_code = rich_helper.get_code_without_tag(all_code, "summary")
all_code = rich_helper.get_code_without_tag(all_code, "details")
all_code = rich_helper.get_code_without_tag(all_code, "span")
all_code = rich_helper.get_code_without_tag(all_code, "div")
all_code = rich_helper.get_code_without_tag(all_code, "img")
all_code = rich_helper.get_code_without_tag(all_code, "li")
all_code = rich_helper.get_code_without_tag(all_code, "ul")
all_code = rich_helper.get_code_without_tag(all_code, "a")
all_code = rich_helper.get_code_without_tag(all_code, "p")
all_code = all_code.replace("<br />", "\n")
print_success = rich_helper.display_markdown(all_code)
if all_code.endswith("\n"):
print() # Because "rich" skips the last line if new-line
elif magic_syntax:
print_success = rich_helper.display_code(magic_syntax)
if not magic_syntax or not print_success:
for line in code_lines:
print(line)
print(dashes)
# ----------------------------------------
if __name__ == "__main__":
invalid_run_command()