|
9 | 9 |
|
10 | 10 | from argparse import ArgumentParser |
11 | 11 | from code import InteractiveConsole |
| 12 | +from contextlib import contextmanager |
12 | 13 | from textwrap import dedent |
13 | 14 |
|
14 | 15 |
|
@@ -62,6 +63,59 @@ def runsource(self, source, filename="<input>", symbol="single"): |
62 | 63 | return False |
63 | 64 |
|
64 | 65 |
|
| 66 | +def _complete(text, state): |
| 67 | + keywords = ["ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ALWAYS", |
| 68 | + "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", |
| 69 | + "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", |
| 70 | + "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", |
| 71 | + "CONSTRAINT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", |
| 72 | + "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", |
| 73 | + "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", |
| 74 | + "DISTINCT", "DO", "DROP", "EACH", "ELSE", "END", "ESCAPE", |
| 75 | + "EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", |
| 76 | + "FILTER", "FIRST", "FOLLOWING", "FOR", "FOREIGN", "FROM", |
| 77 | + "FULL", "GENERATED", "GLOB", "GROUP", "GROUPS", "HAVING", "IF", |
| 78 | + "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", |
| 79 | + "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", |
| 80 | + "ISNULL", "JOIN", "KEY", "LAST", "LEFT", "LIKE", "LIMIT", |
| 81 | + "MATCH", "MATERIALIZED", "NATURAL", "NO", "NOT", "NOTHING", |
| 82 | + "NOTNULL", "NULL", "NULLS", "OF", "OFFSET", "ON", "OR", |
| 83 | + "ORDER", "OTHERS", "OUTER", "OVER", "PARTITION", "PLAN", |
| 84 | + "PRAGMA", "PRECEDING", "PRIMARY", "QUERY", "RAISE", "RANGE", |
| 85 | + "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", |
| 86 | + "RENAME", "REPLACE", "RESTRICT", "RETURNING", "RIGHT", |
| 87 | + "ROLLBACK", "ROW", "ROWS", "SAVEPOINT", "SELECT", "SET", |
| 88 | + "TABLE", "TEMP", "TEMPORARY", "THEN", "TIES", "TO", |
| 89 | + "TRANSACTION", "TRIGGER", "UNBOUNDED", "UNION", "UNIQUE", |
| 90 | + "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", |
| 91 | + "WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT"] |
| 92 | + options = [c + " " for c in keywords if c.startswith(text.upper())] |
| 93 | + try: |
| 94 | + return options[state] |
| 95 | + except IndexError: |
| 96 | + return None |
| 97 | + |
| 98 | +@contextmanager |
| 99 | +def _enable_completer(): |
| 100 | + try: |
| 101 | + import readline |
| 102 | + except ImportError: |
| 103 | + yield |
| 104 | + return |
| 105 | + |
| 106 | + old_completer = readline.get_completer() |
| 107 | + try: |
| 108 | + readline.set_completer(_complete) |
| 109 | + if readline.backend == "editline": |
| 110 | + # libedit uses "^I" instead of "tab" |
| 111 | + command_string = "bind ^I rl_complete" |
| 112 | + else: |
| 113 | + command_string = "tab: complete" |
| 114 | + readline.parse_and_bind(command_string) |
| 115 | + yield |
| 116 | + finally: |
| 117 | + readline.set_completer(old_completer) |
| 118 | + |
65 | 119 | def main(*args): |
66 | 120 | parser = ArgumentParser( |
67 | 121 | description="Python sqlite3 CLI", |
@@ -114,12 +168,9 @@ def main(*args): |
114 | 168 | execute(con, args.sql, suppress_errors=False) |
115 | 169 | else: |
116 | 170 | # No SQL provided; start the REPL. |
117 | | - console = SqliteInteractiveConsole(con) |
118 | | - try: |
119 | | - import readline # noqa: F401 |
120 | | - except ImportError: |
121 | | - pass |
122 | | - console.interact(banner, exitmsg="") |
| 171 | + with _enable_completer(): |
| 172 | + console = SqliteInteractiveConsole(con) |
| 173 | + console.interact(banner, exitmsg="") |
123 | 174 | finally: |
124 | 175 | con.close() |
125 | 176 |
|
|
0 commit comments