Path: blob/main/python/pylang/tools/completer.ts
1396 views
/*1* Copyright (C) 2021 William Stein <[email protected]>2* Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>3*4* Distributed under terms of the BSD license5*/67import { runInThisContext } from "vm";8import { Compiler } from "./compiler";910type Token = any;1112export default function Completer(compiler: Compiler) {13const allKeywords: string[] = compiler.ALL_KEYWORDS.split(" ");1415function globalNames(): string[] {16try {17const names: string[] = runInThisContext(18"Object.getOwnPropertyNames((() => this)())"19);20return [...new Set(names.concat(allKeywords))].sort();21} catch (e) {22console.log(e.stack || e.toString());23}24return [];25}2627function objectNames(obj: any, prefix: string): string[] {28if (obj == null) return [];2930const names: string[] = [];3132function add(o): void {33const items = Object.getOwnPropertyNames(o).filter((name) =>34name.startsWith(prefix)35);36names.push(...items);37}3839let p;40if (typeof obj === "object" || typeof obj === "function") {41add(obj);42p = Object.getPrototypeOf(obj);43} else {44p = obj.constructor?.prototype;45}4647// Walk the prototype chain48try {49// Circular refs possible? Let's guard against that.50for (let sentinel = 0; sentinel < 5 && p != null; sentinel++) {51add(p);52p = Object.getPrototypeOf(p);53}54} catch (_err) {}5556// unique and sorted:57return [...new Set(names)].sort();58}5960function prefixMatches(prefix: string, items: string[]): string[] {61return items.filter((item) => item.startsWith(prefix)).sort();62}6364function findCompletions(line: string) {65let t;66try {67t = compiler.tokenizer(line, "<repl>");68} catch (_err) {69return [];70}71const tokens: Token[] = [];72let token: Token;73while (true) {74try {75token = t();76} catch (_err) {77return [];78}79if (token.type === "eof") break;80if (token.type === "punc" && "(){},;:".includes(token.value)) {81// empties the tokens since we care about what's next only82tokens.splice(0, tokens.length);83}84tokens.push(token);85}86if (tokens.length == 0) {87// New line or trailing space88return [globalNames(), ""];89}90let lastTok: any = tokens[tokens.length - 1];91if (92lastTok.value === "." ||93(lastTok.type === "name" && compiler.IDENTIFIER_PAT.test(lastTok.value))94) {95lastTok = lastTok.value;96if (lastTok === ".") {97tokens.push({ value: "" });98lastTok = "";99}100if (tokens.length > 1 && tokens[tokens.length - 2].value === ".") {101// A compound expression102let prefix = "";103let result;104for (const tok of tokens.slice(0, tokens.length - 2)) {105prefix += tok.value;106}107if (prefix) {108try {109result = runInThisContext(prefix);110} catch (e) {111return [];112}113return [objectNames(result, lastTok), lastTok];114}115} else {116return [prefixMatches(lastTok, globalNames()), lastTok];117}118}119return [];120}121122return findCompletions;123}124125126