Skip to content

Commit

Permalink
v0.2.1
Browse files Browse the repository at this point in the history
Updates documentation by adding docstrings and improving examples
  • Loading branch information
Moosems committed May 30, 2024
1 parent 9fb2f0b commit 41b83fe
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 9 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ The `COMMANDS` list contains all valid commands to request server output from. I
- "replacements"
- "highlight"

### `generic_tokens`

The `generic_tokens` list is a list of strings that define all of the generic token types returned by the server which can be mapped to colors for syntax highlighting.

### `Request` and `Response` TypedDict classes

The `Request` and `Response` TypedDict classes allow for type checking when handling output from salve_ipc.
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

context = IPC()

context.add_file(
context.update_file(
"test", "test file with testing words which should return test then testing"
)

Expand Down
2 changes: 1 addition & 1 deletion salve_ipc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .highlight import Token, tokens_from_result # noqa: F401
from .highlight import Token, tokens_from_result, generic_tokens # noqa: F401
from .ipc import IPC # noqa: F401
from .misc import COMMANDS, Request, Response # noqa: F401
from .server_functions import is_unicode_letter # noqa: F401
2 changes: 1 addition & 1 deletion salve_ipc/highlight/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .highlight import Token, get_highlights, tokens_from_result # noqa: F401
from .highlight import Token, get_highlights, tokens_from_result, generic_tokens # noqa: F401
4 changes: 4 additions & 0 deletions salve_ipc/highlight/highlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@

@dataclass
class Token:
"""Generic Token class that makes highlighting files simple and easy"""
start_index: tuple[int, int] # line, column
token_length: int
highlight_type: str


def tokens_from_result(result: list[str]) -> list[Token]:
"""Returns a list of Token's given as a result (converted to str) that can be used for highlighting"""
tokens: list[Token] = []
for token in result:
try:
Expand All @@ -64,6 +66,7 @@ def tokens_from_result(result: list[str]) -> list[Token]:


def get_new_token_type(old_token: str) -> str:
"""Turns pygments token types into a generic predefined Token"""
new_type: str = generic_tokens[0]
for index, token in enumerate(default_tokens):
if token.startswith(old_token):
Expand All @@ -73,6 +76,7 @@ def get_new_token_type(old_token: str) -> str:


def get_highlights(full_text: str, language: str = "text") -> list:
"""Gets pygments tokens from text provided in language proved and converts them to Token's"""
lexer: Lexer = get_lexer_by_name(language)
new_tokens: list[Token] = []
og_tokens: list[tuple[_TokenType, str]] = list(lex(full_text, lexer))
Expand Down
24 changes: 24 additions & 0 deletions salve_ipc/ipc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@


class IPC:
"""The IPC class is used to talk to the server and run commands ("autocomplete", "replacements", and "highlight"). The public API includes the following methods:
- IPC.ping()
- IPC.request()
- IPC.cancel_request()
- IPC.update_file()
- IPC.remove_file()
- IPC.kill_IPC()
"""

def __init__(self, id_max: int = 15_000) -> None:
self.used_ids: list[int] = []
self.id_max = id_max
Expand All @@ -25,6 +34,7 @@ def __init__(self, id_max: int = 15_000) -> None:
self.create_server()

def create_server(self) -> None:
"""Creates the main_server through a subprocess - internal API"""
server_file: Path = Path(__file__).parent / "server.py"
server = Popen(["python3", str(server_file)], stdin=PIPE, stdout=PIPE)
set_blocking(server.stdout.fileno(), False) # type: ignore
Expand All @@ -37,23 +47,27 @@ def create_server(self) -> None:
self.add_file(filename, data)

def check_server(self) -> None:
"""Checks that the main_server is alive - internal API"""
if self.main_server.poll() is not None:
self.create_server()

def get_server_file(self, file: str) -> IO:
"""Returns the main_server stdin/stdout based on the argument provided ("stdin"/"stdout") - internal API"""
self.check_server()
if file == "stdout":
return self.main_server.stdout # type: ignore
return self.main_server.stdin # type: ignore

def send_message(self, message: Message) -> None:
"""Sends a Message to the main_server as provided by the argument message - internal API"""
json_request: str = dumps(message)

server_stdin = self.get_server_file("stdin")
server_stdin.write(f"{json_request}\n".encode())
server_stdin.flush()

def create_message(self, type: str, **kwargs) -> None:
"""Creates a Message based on the args and kwawrgs provided. Highly flexible. - internal API"""
id = randint(1, self.id_max) # 0 is reserved for the empty case
while id in self.used_ids:
id = randint(1, self.id_max)
Expand Down Expand Up @@ -90,6 +104,7 @@ def create_message(self, type: str, **kwargs) -> None:
self.send_message(ping)

def ping(self) -> None:
"""Pings the main_server to keep it alive - external API"""
self.create_message("ping")

def request(
Expand All @@ -100,6 +115,7 @@ def request(
current_word: str = "",
language: str = "Text",
) -> None:
"""Sends the main_server a request of type command with given kwargs - external API"""
if command not in COMMANDS:
self.kill_IPC()
raise Exception(
Expand All @@ -116,6 +132,7 @@ def request(
)

def cancel_request(self, command: str):
"""Cancels a request of type command - external API"""
if command not in COMMANDS:
self.kill_IPC()
raise Exception(
Expand All @@ -125,6 +142,7 @@ def cancel_request(self, command: str):
self.current_ids[command] = 0

def parse_line(self, line: str) -> None:
"""Parses main_server output line and discards useless responses - internal API"""
response_json: Response = loads(line)
id = response_json["id"]
self.used_ids.remove(id)
Expand All @@ -140,12 +158,14 @@ def parse_line(self, line: str) -> None:
self.newest_responses[command] = response_json

def check_responses(self) -> None:
"""Checks all main_server output by calling IPC.parse_line() on each response - internal API"""
server_stdout: IO = self.get_server_file("stdout")

for line in server_stdout: # type: ignore
self.parse_line(line)

def get_response(self, command: str) -> Response | None:
"""Runs IPC.check_responses() and returns the current response of type command if it has been returned - external API"""
if command not in COMMANDS:
self.kill_IPC()
raise Exception(
Expand All @@ -160,6 +180,7 @@ def get_response(self, command: str) -> Response | None:
return response

def add_file(self, filename: str, current_state: str) -> None:
"""Adds a file to the main_server's file list - internal API"""
if filename in self.files.keys():
return

Expand All @@ -170,6 +191,7 @@ def add_file(self, filename: str, current_state: str) -> None:
self.create_message("notification", filename=filename, diff=diff)

def update_file(self, filename: str, current_state: str) -> None:
"""Updates files if they are already in the system or adds them if not - external API"""
self.add_file(filename, current_state)

self.files[filename] = current_state
Expand All @@ -183,6 +205,7 @@ def update_file(self, filename: str, current_state: str) -> None:
self.create_message("notification", filename=filename, diff=diff)

def remove_file(self, filename: str) -> None:
"""Removes a file from the main_server - external API"""
if filename not in list(self.files.keys()):
self.kill_IPC()
raise Exception(
Expand All @@ -192,4 +215,5 @@ def remove_file(self, filename: str) -> None:
self.create_message("notification", remove=True, filename=filename)

def kill_IPC(self) -> None:
"""Kills the main_server when salve_ipc's services are no longer required - external API"""
self.main_server.kill()
14 changes: 10 additions & 4 deletions salve_ipc/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,34 @@


class Message(TypedDict):
"""Base class for messages in and out of the server"""
id: int
type: str # Can be "ping", "request", "response", "cancelled", "notification"


class Request(Message):
"""Request results/output from the server with command specific input"""
command: str # Can only be commands in COMMANDS
file: str
expected_keywords: NotRequired[list[str]]
current_word: NotRequired[str]
language: NotRequired[str]
expected_keywords: NotRequired[list[str]] # autocomplete, replacements
current_word: NotRequired[str] # autocomplete, replacements
language: NotRequired[str] # highlight


class Ping(Message): ...
class Ping(Message):
"""Not really different from a standard Message but the Ping type allows for nice differentiation"""
...


class Notification(Message):
"""Notifies the server to add/update/remove a file for usage in fulfilling commands"""
filename: str
remove: bool
diff: NotRequired[str]


class Response(Message):
"""Server responses to requests, notifications, and pings"""
cancelled: bool
command: NotRequired[str]
result: NotRequired[list[str]]
1 change: 1 addition & 0 deletions salve_ipc/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@


class Handler:
"""Handles input from the user and returns output from special functions designed to make the job easy. Not an external API."""
def __init__(self) -> None:
set_blocking(stdin.fileno(), False)
set_blocking(stdout.fileno(), False)
Expand Down
5 changes: 4 additions & 1 deletion salve_ipc/server_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@


def is_unicode_letter(char: str) -> bool:
"""Returns a boolean value of whether a given unicode char is a letter or not (includes "_" for code completion reasons)"""
return char == "_" or category(char).startswith("L")


def find_words(full_text: str) -> list[str]:
"""Returns a list of all words in a given piece of text"""
words_list = []
current_word = ""

Expand All @@ -33,7 +35,7 @@ def find_words(full_text: str) -> list[str]:
def find_autocompletions(
full_text: str, expected_keywords: list[str], current_word: str
) -> list[str]:
"""Returns a list of autocompletions based on the word"""
"""Returns a list of autocompletions based on the word, text, and language keywords"""

words_in_text: list[str] = find_words(full_text)

Expand Down Expand Up @@ -61,6 +63,7 @@ def find_autocompletions(
def get_replacements(
full_text: str, expected_keywords: list[str], replaceable_word: str
) -> list[str]:
"""Returns a list of possible and plausible replacements for a given word"""
# Get all words in file
starter_words = find_words(full_text)
starter_words += (
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name="salve_ipc",
version="0.2.0",
version="0.2.1",
description="A module that makes easily provides autocompletions, replacement suggestions, and syntax highlighting to your code editor",
author="Moosems",
author_email="[email protected]",
Expand Down

0 comments on commit 41b83fe

Please sign in to comment.