diff --git a/Dockerfile b/Dockerfile index db94c67..6e82914 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,8 @@ WORKDIR /app COPY . /app -RUN pip install --upgrade pip \ - && pip install . \ +RUN pip install --upgrade pip poetry \ + && poetry install \ && apt-get clean ENTRYPOINT ["searchcode"] \ No newline at end of file diff --git a/README.md b/README.md index db12c18..72720a5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ sc search "import module" ```python from pprint import pprint -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") search = sc.search(query="import module") @@ -70,7 +70,7 @@ searchcode "import module" ```python from pprint import pprint -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") search = sc.search(query="import module") @@ -92,7 +92,7 @@ searchcode "import module" --languages java,javascript ```python from pprint import pprint -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") search = sc.search(query="import module", languages=["Java", "JavaScript"]) @@ -115,7 +115,7 @@ searchcode "import module" --sources bitbucket,codeplex ```python from pprint import pprint -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") search = sc.search(query="import module", sources=["BitBucket", "CodePlex"]) @@ -138,7 +138,7 @@ searchcode "import module" --lines-of-code-gt 500 --lines-of-code-lt 1000 ```python from pprint import pprint -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") search = sc.search(query="import module", lines_of_code_gt=500, lines_of_code_lt=1000) @@ -161,7 +161,7 @@ searchcode "import module" --callback myCallback ```python from pprint import pprint -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") search = sc.search(query="import module", callback="myCallback") @@ -222,7 +222,7 @@ searchode code 4061576 ```python -from searchcode import Searchcode +from src.searchcode import Searchcode sc = Searchcode(user_agent="My-Searchcode-script") data = sc.code(4061576) diff --git a/pyproject.toml b/pyproject.toml index 5f90543..142df7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "searchcode" -version = "0.5.0" +version = "0.5.1" description = "Simple, comprehensive code search." authors = ["Ritchie Mwewa "] license = "GPLv3+" @@ -30,5 +30,5 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] -sc = "searchcode.cli:cli" -searchcode = "searchcode.cli:cli" \ No newline at end of file +sc = "searchcode.__app:cli" +searchcode = "searchcode.__app:cli" \ No newline at end of file diff --git a/searchcode/cli.py b/src/searchcode/__app.py similarity index 57% rename from searchcode/cli.py rename to src/searchcode/__app.py index 377502c..2c96917 100644 --- a/searchcode/cli.py +++ b/src/searchcode/__app.py @@ -15,19 +15,22 @@ along with this program. If not, see . """ -import os -import subprocess import typing as t -from types import SimpleNamespace import rich_click as click -from rich import box from rich.console import Console -from rich.panel import Panel from rich.pretty import pprint from rich.syntax import Syntax -from . import License, __pkg__, __version__ +from . import License +from .__lib import ( + __pkg__, + __version__, + update_window_title, + clear_screen, + print_jsonp, + print_panels, +) from .api import Searchcode sc = Searchcode(user_agent=f"{__pkg__}-sdk/cli") @@ -46,7 +49,7 @@ def cli(): Simple, comprehensive code search. """ - __update_window_title("Source code search engine.") + update_window_title(text="Source code search engine.") @cli.command("license") @@ -59,8 +62,8 @@ def licence( """ Show license information """ - __clear_screen() - __update_window_title( + clear_screen() + update_window_title( text="Terms and Conditions" if conditions else "Warranty" if warranty else None ) if conditions: @@ -133,8 +136,8 @@ def search( e.g., sc search "import module" """ - __clear_screen() - __update_window_title(text=query) + clear_screen() + update_window_title(text=query) with console.status( f"Querying code index with search string: [green]{query}[/]..." @@ -154,9 +157,9 @@ def search( ) ( - __print_jsonp(jsonp=response) + print_jsonp(jsonp=response) if callback - else (pprint(response) if pretty else __print_panels(data=response.results)) + else (pprint(response) if pretty else print_panels(data=response.results)) ) @@ -168,8 +171,8 @@ def code(id: int): e.g., sc code 4061576 """ - __clear_screen() - __update_window_title(text=str(id)) + clear_screen() + update_window_title(text=str(id)) with console.status(f"Fetching data for code file with ID: [cyan]{id}[/]..."): data = sc.code(id) lines = data.code @@ -179,86 +182,3 @@ def code(id: int): code=lines, lexer=language, line_numbers=True, theme="dracula" ) console.print(syntax) - - -def __print_jsonp(jsonp: str) -> None: - """ - Pretty-prints a raw JSONP string. - - :param jsonp: A complete JSONP string. - """ - syntax = Syntax(jsonp, "text", line_numbers=True) - console.print(syntax) - - -def __print_panels(data: t.List[SimpleNamespace]): - """ - Render a list of code records as rich panels with syntax highlighting. - Line numbers are preserved and displayed alongside code content. - - :param data: A list of dictionaries, where each dictionary represents a code record - """ - - def extract_code_string_with_linenumbers(lines_dict: t.Dict[str, str]) -> str: - """ - Convert a dictionary of line_number: code_line into a single - multiline string sorted by line number. - - Each line is right-aligned to maintain visual alignment in output. - - :param lines_dict: Dictionary where keys are line numbers (as strings) and values are lines of code. - :return: Multiline string with original line numbers included. - """ - sorted_lines = sorted(lines_dict.items(), key=lambda x: int(x[0])) - numbered_lines = [ - f"{line_no.rjust(4)} {line.rstrip()}" for line_no, line in sorted_lines - ] - return "\n".join(numbered_lines) - - for item in data: - filename = item.filename - repo = item.repo - language = item.language - lines_count = item.linescount - lines = item.lines - - code_string = extract_code_string_with_linenumbers(lines_dict=lines.__dict__) - - syntax = Syntax( - code=code_string, - lexer=language, - word_wrap=False, - indent_guides=True, - theme="dracula", - ) - - panel = Panel( - renderable=syntax, - box=box.ROUNDED, - title=f"[bold]{filename}[/] ([blue]{repo}[/]) {language} ⸱ [cyan]{lines_count}[/] lines", - highlight=True, - ) - - console.print(panel) - - -def __update_window_title(text: str): - """ - Update the current window title with the specified text. - - :param text: Text to update the window with. - """ - console.set_window_title(f"{__pkg__.capitalize()} - {text}") - - -def __clear_screen(): - """ - Clear the screen. - - Not using console.clear() because it doesn't really clear the screen. - It instead creates a space between the items on top and below, - then moves the cursor to the items on the bottom, thus creating the illusion of a "cleared screen". - - Using subprocess might be a bad idea, but I'm yet to see how bad of an idea that is. - """ - subprocess.run(["cls" if os.name == "nt" else "clear"]) diff --git a/searchcode/__init__.py b/src/searchcode/__init__.py similarity index 98% rename from searchcode/__init__.py rename to src/searchcode/__init__.py index 483e5ac..6827e3e 100644 --- a/searchcode/__init__.py +++ b/src/searchcode/__init__.py @@ -20,7 +20,7 @@ from .api import Searchcode __pkg__ = "searchcode" -__version__ = "0.5.0" +__version__ = "0.5.1" __author__ = "Ritchie Mwewa" diff --git a/src/searchcode/__lib.py b/src/searchcode/__lib.py new file mode 100644 index 0000000..7bde606 --- /dev/null +++ b/src/searchcode/__lib.py @@ -0,0 +1,96 @@ +import os +import subprocess +import typing as t +from types import SimpleNamespace + +from rich import box +from rich.console import Console +from rich.panel import Panel +from rich.syntax import Syntax + +from . import __pkg__, __version__ + +console = Console() + + +def print_jsonp(jsonp: str) -> None: + """ + Pretty-prints a raw JSONP string. + + :param jsonp: A complete JSONP string. + """ + syntax = Syntax(jsonp, "text", line_numbers=True) + console.print(syntax) + + +def print_panels(data: t.List[SimpleNamespace]): + """ + Render a list of code records as rich panels with syntax highlighting. + Line numbers are preserved and displayed alongside code content. + + :param data: A list of dictionaries, where each dictionary represents a code record + """ + + def extract_code_string_with_linenumbers(lines_dict: t.Dict[str, str]) -> str: + """ + Convert a dictionary of line_number: code_line into a single + multiline string sorted by line number. + + Each line is right-aligned to maintain visual alignment in output. + + :param lines_dict: Dictionary where keys are line numbers (as strings) and values are lines of code. + :return: Multiline string with original line numbers included. + """ + sorted_lines = sorted(lines_dict.items(), key=lambda x: int(x[0])) + numbered_lines = [ + f"{line_no.rjust(4)} {line.rstrip()}" for line_no, line in sorted_lines + ] + return "\n".join(numbered_lines) + + for item in data: + filename = item.filename + repo = item.repo + language = item.language + lines_count = item.linescount + lines = item.lines + + code_string = extract_code_string_with_linenumbers(lines_dict=lines.__dict__) + + syntax = Syntax( + code=code_string, + lexer=language, + word_wrap=False, + indent_guides=True, + theme="dracula", + ) + + panel = Panel( + renderable=syntax, + box=box.ROUNDED, + title=f"[bold]{filename}[/] ([blue]{repo}[/]) {language} ⸱ [cyan]{lines_count}[/] lines", + highlight=True, + ) + + console.print(panel) + + +def update_window_title(text: str): + """ + Update the current window title with the specified text. + + :param text: Text to update the window with. + """ + console.set_window_title(f"{__pkg__.capitalize()} v{__version__} - {text}") + + +def clear_screen(): + """ + Clear the screen. + + Not using console.clear() because it doesn't really clear the screen. + It instead creates a space between the items on top and below, + then moves the cursor to the items on the bottom, thus creating the illusion of a "cleared screen". + + Using subprocess might be a bad idea, but I'm yet to see how bad of an idea that is. + """ + subprocess.run(["cls" if os.name == "nt" else "clear"]) diff --git a/searchcode/api.py b/src/searchcode/api.py similarity index 100% rename from searchcode/api.py rename to src/searchcode/api.py diff --git a/searchcode/filters.py b/src/searchcode/filters.py similarity index 100% rename from searchcode/filters.py rename to src/searchcode/filters.py