feat(repl): add auto-completer
This commit is contained in:
0
src/completer/__init__.py
Normal file
0
src/completer/__init__.py
Normal file
38
src/completer/completer.py
Normal file
38
src/completer/completer.py
Normal 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)
|
||||
]
|
||||
19
src/repl.py
19
src/repl.py
@@ -3,6 +3,7 @@ import readline
|
||||
from pathlib import Path
|
||||
|
||||
from src.ast.stmt import Stmt
|
||||
from src.completer.completer import Completer
|
||||
from src.interpreter.interpreter import Interpreter
|
||||
from src.interpreter.resolver import Resolver
|
||||
from src.parser.parser import Parser
|
||||
@@ -16,6 +17,8 @@ class REPL:
|
||||
|
||||
def __init__(self):
|
||||
self.interpreter: Interpreter = Interpreter()
|
||||
self.completer: Completer = Completer(self.interpreter)
|
||||
self.buf: str = ""
|
||||
|
||||
@staticmethod
|
||||
def greet():
|
||||
@@ -38,6 +41,10 @@ class REPL:
|
||||
|
||||
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):
|
||||
line: str
|
||||
tokens: list[Token]
|
||||
@@ -45,22 +52,22 @@ class REPL:
|
||||
|
||||
self.greet()
|
||||
self.init_history()
|
||||
self.init_completer()
|
||||
|
||||
buf: str = ""
|
||||
while True:
|
||||
try:
|
||||
line = input(">>> " if len(buf) == 0 else "... ")
|
||||
line = input(">>> " if len(self.buf) == 0 else "... ")
|
||||
except EOFError:
|
||||
print()
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
buf = ""
|
||||
self.buf = ""
|
||||
print()
|
||||
continue
|
||||
|
||||
buf += line + "\n"
|
||||
self.buf += line + "\n"
|
||||
try:
|
||||
tokens = Lexer(buf).process()
|
||||
tokens = Lexer(self.buf).process()
|
||||
except:
|
||||
continue
|
||||
if Pebble.had_error:
|
||||
@@ -71,7 +78,7 @@ class REPL:
|
||||
continue
|
||||
|
||||
program = Parser(tokens).parse()
|
||||
buf = ""
|
||||
self.buf = ""
|
||||
|
||||
if Pebble.had_error:
|
||||
Pebble.had_error = False
|
||||
|
||||
Reference in New Issue
Block a user