Skip to content

Commit bc6ff8c

Browse files
committed
Add license commands to allow viewers to read license terms and conditions and warrantly. ALso, added status message because who doesnt like to know what's really going on?
1 parent 4c4acdf commit bc6ff8c

7 files changed

Lines changed: 802 additions & 64 deletions

File tree

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
652652
If the program does terminal interaction, make it output a short
653653
notice like this when it starts in an interactive mode:
654654

655-
searchcode-cli Copyright (C) 2023 Richard Mwewa
655+
<name> Copyright (C) <year> <author>
656656
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657657
This is free software, and you are welcome to redistribute it
658658
under certain conditions; type `show c' for details.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "searchcode"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
description = "Simple, comprehensive code search."
55
authors = ["Ritchie Mwewa <rly0nheart@duck.com>"]
66
license = "GPLv3+"
@@ -31,5 +31,5 @@ requires = ["poetry-core"]
3131
build-backend = "poetry.core.masonry.api"
3232

3333
[tool.poetry.scripts]
34-
sc = "searchcode.cli:cli" # shortened command
34+
sc = "searchcode.cli:cli"
3535
searchcode = "searchcode.cli:cli"

searchcode/__init__.py

Lines changed: 594 additions & 0 deletions
Large diffs are not rendered by default.

searchcode/api.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
"""
2+
Copyright (C) 2024 Ritchie Mwewa
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
"""
17+
118
import typing as t
219
from platform import python_version, platform
320

searchcode/cli.py

Lines changed: 154 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,77 @@
1+
"""
2+
Copyright (C) 2024 Ritchie Mwewa
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
"""
17+
18+
import os
19+
import subprocess
120
import typing as t
221

322
import rich_click as click
4-
from rich import print as rprint, box
23+
from rich import box
24+
from rich.console import Console
25+
from rich.panel import Panel
526
from rich.pretty import pprint
627
from rich.syntax import Syntax
7-
from rich.table import Table
828
from whats_that_code.election import guess_language_all_methods
929

30+
from . import License, __pkg__, __version__
1031
from .api import Searchcode
1132

12-
sc = Searchcode(user_agent="searchCode-sdk/cli")
33+
sc = Searchcode(user_agent=f"{__pkg__}-sdk/cli")
1334

1435
__all__ = ["cli"]
1536

37+
console = Console(highlight=True)
38+
1639

1740
@click.group()
41+
@click.version_option(version=__version__, package_name=__pkg__)
1842
def cli():
1943
"""
2044
Searchcode
2145
2246
Simple, comprehensive code search.
2347
"""
24-
...
48+
49+
__update_window_title("Source code search engine.")
50+
51+
52+
@cli.command("license")
53+
@click.option("--conditions", help="License terms and conditions.", is_flag=True)
54+
@click.option("--warranty", help="License warranty.", is_flag=True)
55+
def licence(conditions: t.Optional[bool], warranty: t.Optional[bool]):
56+
"""
57+
Show license
58+
"""
59+
__clear_screen()
60+
__update_window_title(
61+
text="Terms and Conditions" if conditions else "Warranty" if warranty else None
62+
)
63+
if conditions:
64+
console.print(
65+
License.terms_and_conditions,
66+
justify="center",
67+
style="on #272822", # monokai themed background :)
68+
)
69+
if warranty:
70+
console.print(
71+
License.warranty,
72+
justify="center",
73+
style="on #272822",
74+
)
2575

2676

2777
@cli.command()
@@ -31,7 +81,7 @@ def cli():
3181
"--page",
3282
type=int,
3383
default=0,
34-
help="Start page number (defaults to 0).",
84+
help="Start page (defaults to 0).",
3585
)
3686
@click.option(
3787
"--per-page",
@@ -76,33 +126,37 @@ def search(
76126
callback: t.Optional[str] = None,
77127
):
78128
"""
79-
Query the code index and (returns 100 results by default).
129+
Query the code index (returns 100 results by default).
80130
81-
e.g., searchcode search "import module"
131+
e.g., sc search "import module"
82132
"""
83-
languages = languages.split(",") if languages else None
84-
sources = sources.split(",") if sources else None
133+
__clear_screen()
134+
__update_window_title(text=query)
85135

86-
results = sc.search(
87-
query=query,
88-
page=page,
89-
per_page=per_page,
90-
languages=languages,
91-
sources=sources,
92-
lines_of_code_lt=lines_of_code_lt,
93-
lines_of_code_gt=lines_of_code_gt,
94-
callback=callback,
95-
)
136+
with console.status(f"Querying code index with [green]{query}[/]"):
137+
languages = languages.split(",") if languages else None
138+
sources = sources.split(",") if sources else None
96139

97-
(
98-
__print_jsonp(jsonp=results)
99-
if callback
100-
else (
101-
pprint(results)
102-
if pretty
103-
else __print_table(records=results["results"], ignore_keys=["lines"])
140+
results = sc.search(
141+
query=query,
142+
page=page,
143+
per_page=per_page,
144+
languages=languages,
145+
sources=sources,
146+
lines_of_code_lt=lines_of_code_lt,
147+
lines_of_code_gt=lines_of_code_gt,
148+
callback=callback,
149+
)
150+
151+
(
152+
__print_jsonp(jsonp=results)
153+
if callback
154+
else (
155+
pprint(results)
156+
if pretty
157+
else __print_panels(data=results.get("results"))
158+
)
104159
)
105-
)
106160

107161

108162
@cli.command()
@@ -111,55 +165,94 @@ def code(id: int):
111165
"""
112166
Get the raw data from a code file.
113167
114-
e.g., searchcode code 4061576
168+
e.g., sc code 4061576
169+
"""
170+
__clear_screen()
171+
__update_window_title(text=str(id))
172+
with console.status(f"Retrieving code data for [cyan]{id}[/]") as status:
173+
code_data = sc.code(id)
174+
if code_data:
175+
status.update("Determining code language")
176+
language = guess_language_all_methods(code=code_data)
177+
syntax = Syntax(code=code_data, lexer=language, line_numbers=True)
178+
console.print(syntax)
179+
180+
181+
def __print_jsonp(jsonp: str) -> None:
182+
"""
183+
Pretty-prints a raw JSONP string.
184+
185+
:param jsonp: A complete JSONP string.
115186
"""
116-
code_data = sc.code(id)
117-
if code_data:
118-
language = guess_language_all_methods(code=code_data)
119-
syntax = Syntax(code=code_data, lexer=language, line_numbers=True)
120-
rprint(syntax)
187+
syntax = Syntax(jsonp, "text", line_numbers=True)
188+
console.print(syntax)
121189

122190

123-
def __print_table(records: t.List[t.Dict], ignore_keys: t.List[str] = None) -> None:
191+
def __print_panels(data: t.List[t.Dict]):
124192
"""
125-
Creates a rich table from a list of dict objects,
126-
ignoring specified keys.
193+
Render a list of code records as rich panels with syntax highlighting.
194+
Line numbers are preserved and displayed alongside code content.
127195
128-
:param records: List of dict objects.
129-
:param ignore_keys: List of keys to exclude from the table.
130-
:return: None. Prints the table using rich.
196+
:param data: A list of dictionaries, where each dictionary represents a code record
131197
"""
132-
if not records:
133-
raise ValueError("Data must be a non-empty list of dict objects.")
134198

135-
ignore_keys = ignore_keys or []
199+
def extract_code_string_with_linenumbers(lines_dict: t.Dict[str, str]) -> str:
200+
"""
201+
Convert a dictionary of line_number: code_line into a single
202+
multiline string sorted by line number.
136203
137-
# Collect all unique keys across all records, excluding ignored ones
138-
all_keys = set()
139-
for record in records:
140-
all_keys.update(key for key in record.keys() if key not in ignore_keys)
204+
Each line is right-aligned to maintain visual alignment in output.
141205
142-
columns = sorted(all_keys)
206+
:param lines_dict: Dictionary where keys are line numbers (as strings) and values are lines of code.
207+
:return: Multiline string with original line numbers included.
208+
"""
209+
sorted_lines = sorted(lines_dict.items(), key=lambda x: int(x[0]))
210+
numbered_lines = [
211+
f"{line_no.rjust(4)} {line.rstrip()}" for line_no, line in sorted_lines
212+
]
213+
return "\n".join(numbered_lines)
143214

144-
table = Table(box=box.ROUNDED, highlight=True, header_style="bold")
215+
for item in data:
216+
filename = item.get("filename", "Unknown")
217+
repo = item.get("repo", "Unknown")
218+
language = item.get("language", "text")
219+
lines_count = item.get("linescount", "??")
145220

146-
for index, column in enumerate(columns):
147-
style = "dim" if index == 0 else None
148-
table.add_column(column.capitalize(), style=style)
221+
code_string = extract_code_string_with_linenumbers(
222+
lines_dict=item.get("lines", {})
223+
)
149224

150-
for record in records:
151-
data = record
152-
row = [str(data.get(column, "")) for column in columns]
153-
table.add_row(*row)
225+
syntax = Syntax(
226+
code=code_string, lexer=language, word_wrap=False, indent_guides=True
227+
)
154228

155-
rprint(table)
229+
panel = Panel(
230+
renderable=syntax,
231+
box=box.ROUNDED,
232+
title=f"[bold]{filename}[/] ([blue]{repo}[/]) {language} ⸱ [cyan]{lines_count}[/] lines",
233+
highlight=True,
234+
)
156235

236+
console.print(panel)
157237

158-
def __print_jsonp(jsonp: str) -> None:
238+
239+
def __update_window_title(text: str):
159240
"""
160-
Pretty-prints a raw JSONP string.
241+
Update the current window title with the specified text.
161242
162-
:param jsonp: A complete JSONP string.
243+
:param text: Text to update the window with.
163244
"""
164-
syntax = Syntax(jsonp, "text", line_numbers=True)
165-
rprint(syntax)
245+
console.set_window_title(f"{__pkg__.capitalize()} - {text}")
246+
247+
248+
def __clear_screen():
249+
"""
250+
Clear the screen.
251+
252+
Not using console.clear() because it doesn't really clear the screen.
253+
It instead creates a space between the items on top and below,
254+
then moves the cursor to the items on the bottom, thus creating the illusion of a "cleared screen".
255+
256+
Using subprocess might be a bad idea, but I'm yet to see how bad of an idea that is.
257+
"""
258+
subprocess.run(["cls" if os.name == "nt" else "clear"])

searchcode/filters.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
"""
2+
Copyright (C) 2024 Ritchie Mwewa
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
"""
17+
118
import typing as t
219

320

tests/test_searchcode.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
"""
2+
Copyright (C) 2024 Ritchie Mwewa
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
"""
17+
118
from searchcode import Searchcode
219

320
sc = Searchcode(user_agent="Pytest")

0 commit comments

Comments
 (0)