feat(repl): add auto-completer

This commit is contained in:
2026-02-08 14:32:41 +01:00
parent 94a3e3cbe2
commit 218fb91053
3 changed files with 51 additions and 6 deletions

View File

View File

@@ -0,0 +1,38 @@
from typing import Optional
from src.interpreter.environment import Environment
from src.interpreter.interpreter import Interpreter
class Completer:
def __init__(self, interpreter: Interpreter):
self.interpreter: Interpreter = interpreter
self.options: list[str] = []
self.matches: list[str] = []
def complete(self, text: str, state: int) -> Optional[str]:
if state == 0:
self.update_matches(text)
try:
return self.matches[state]
except IndexError:
return None
def update_matches(self, text: str):
self.options = []
env: Optional[Environment] = self.interpreter.env
while env is not None:
self.options.extend(env.values.keys())
env = env.enclosing
self.options = sorted(self.options)
if not text:
self.matches = self.options
else:
self.matches = [
opt
for opt in self.options
if opt.startswith(text)
]

View File

@@ -3,6 +3,7 @@ import readline
from pathlib import Path from pathlib import Path
from src.ast.stmt import Stmt from src.ast.stmt import Stmt
from src.completer.completer import Completer
from src.interpreter.interpreter import Interpreter from src.interpreter.interpreter import Interpreter
from src.interpreter.resolver import Resolver from src.interpreter.resolver import Resolver
from src.parser.parser import Parser from src.parser.parser import Parser
@@ -16,6 +17,8 @@ class REPL:
def __init__(self): def __init__(self):
self.interpreter: Interpreter = Interpreter() self.interpreter: Interpreter = Interpreter()
self.completer: Completer = Completer(self.interpreter)
self.buf: str = ""
@staticmethod @staticmethod
def greet(): def greet():
@@ -38,6 +41,10 @@ class REPL:
atexit.register(save, h_len, self.HIST_FILE) atexit.register(save, h_len, self.HIST_FILE)
def init_completer(self):
readline.set_completer(self.completer.complete)
readline.parse_and_bind("tab: complete")
def run(self): def run(self):
line: str line: str
tokens: list[Token] tokens: list[Token]
@@ -45,22 +52,22 @@ class REPL:
self.greet() self.greet()
self.init_history() self.init_history()
self.init_completer()
buf: str = ""
while True: while True:
try: try:
line = input(">>> " if len(buf) == 0 else "... ") line = input(">>> " if len(self.buf) == 0 else "... ")
except EOFError: except EOFError:
print() print()
break break
except KeyboardInterrupt: except KeyboardInterrupt:
buf = "" self.buf = ""
print() print()
continue continue
buf += line + "\n" self.buf += line + "\n"
try: try:
tokens = Lexer(buf).process() tokens = Lexer(self.buf).process()
except: except:
continue continue
if Pebble.had_error: if Pebble.had_error:
@@ -71,7 +78,7 @@ class REPL:
continue continue
program = Parser(tokens).parse() program = Parser(tokens).parse()
buf = "" self.buf = ""
if Pebble.had_error: if Pebble.had_error:
Pebble.had_error = False Pebble.had_error = False