feat(repl): group completions by scope and add keywords
This commit is contained in:
@@ -1,29 +1,39 @@
|
|||||||
from typing import Optional
|
import os
|
||||||
|
import readline
|
||||||
|
import sys
|
||||||
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
from src.interpreter.environment import Environment
|
from src.interpreter.environment import Environment
|
||||||
from src.interpreter.interpreter import Interpreter
|
from src.interpreter.interpreter import Interpreter
|
||||||
|
from src.token.keyword import KEYWORDS
|
||||||
|
|
||||||
|
|
||||||
class Completer:
|
class Completer:
|
||||||
def __init__(self, interpreter: Interpreter):
|
def __init__(self, interpreter: Interpreter):
|
||||||
self.interpreter: Interpreter = interpreter
|
self.interpreter: Interpreter = interpreter
|
||||||
self.options: list[str] = []
|
self.options: list[list[str]] = []
|
||||||
self.matches: list[str] = []
|
self.matches: list[list[str]] = []
|
||||||
|
|
||||||
def complete(self, text: str, state: int) -> Optional[str]:
|
def complete(self, text: str, state: int) -> Optional[str]:
|
||||||
if state == 0:
|
if state == 0:
|
||||||
self.update_matches(text)
|
self.update_matches(text)
|
||||||
try:
|
return self.get_match(state)
|
||||||
return self.matches[state]
|
|
||||||
except IndexError:
|
def get_match(self, state: int) -> Optional[str]:
|
||||||
return None
|
for grp in self.matches:
|
||||||
|
if state < len(grp):
|
||||||
|
return grp[state]
|
||||||
|
state -= len(grp)
|
||||||
|
return None
|
||||||
|
|
||||||
def update_matches(self, text: str):
|
def update_matches(self, text: str):
|
||||||
self.options = []
|
self.options = [
|
||||||
|
list(KEYWORDS.keys())
|
||||||
|
]
|
||||||
|
|
||||||
env: Optional[Environment] = self.interpreter.env
|
env: Optional[Environment] = self.interpreter.env
|
||||||
while env is not None:
|
while env is not None:
|
||||||
self.options.extend(env.values.keys())
|
self.options.append(sorted(env.values.keys()))
|
||||||
env = env.enclosing
|
env = env.enclosing
|
||||||
|
|
||||||
self.options = sorted(self.options)
|
self.options = sorted(self.options)
|
||||||
@@ -32,7 +42,37 @@ class Completer:
|
|||||||
self.matches = self.options
|
self.matches = self.options
|
||||||
else:
|
else:
|
||||||
self.matches = [
|
self.matches = [
|
||||||
opt
|
[
|
||||||
for opt in self.options
|
opt
|
||||||
if opt.startswith(text)
|
for opt in grp
|
||||||
|
if opt.startswith(text)
|
||||||
|
]
|
||||||
|
for grp in self.options
|
||||||
]
|
]
|
||||||
|
self.matches = list(filter(lambda grp: grp, self.matches))
|
||||||
|
|
||||||
|
def display_matches(self, sub: str, matches: Sequence[str], longest_len: int):
|
||||||
|
line_buf = readline.get_line_buffer()
|
||||||
|
cols = os.get_terminal_size()[0]
|
||||||
|
|
||||||
|
for i, grp in enumerate(self.matches):
|
||||||
|
print()
|
||||||
|
self.display_group(grp, cols)
|
||||||
|
|
||||||
|
print(">>> ", end="")
|
||||||
|
print(line_buf, end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def display_group(self, group: list[str], cols: int):
|
||||||
|
width: int = max(map(len, group))
|
||||||
|
width = width * 6 // 5 + 2
|
||||||
|
buf: str = ""
|
||||||
|
for match in group:
|
||||||
|
match = f"{match:{width}}"
|
||||||
|
if len(buf) + width > cols:
|
||||||
|
print(buf.rstrip())
|
||||||
|
buf = ""
|
||||||
|
buf += match
|
||||||
|
|
||||||
|
if buf:
|
||||||
|
print(buf)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class REPL:
|
|||||||
def init_completer(self):
|
def init_completer(self):
|
||||||
readline.set_completer(self.completer.complete)
|
readline.set_completer(self.completer.complete)
|
||||||
readline.parse_and_bind("tab: complete")
|
readline.parse_and_bind("tab: complete")
|
||||||
|
readline.set_completion_display_matches_hook(self.completer.display_matches)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
line: str
|
line: str
|
||||||
|
|||||||
Reference in New Issue
Block a user