Merging upstream version 1.28.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
336c651928
commit
f6ae5d2542
16 changed files with 611 additions and 404 deletions
|
@ -181,7 +181,7 @@ $ sudo dnf install mycli
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Follow the instructions on this blogpost: https://www.codewall.co.uk/installing-using-mycli-on-windows/
|
Follow the instructions on this blogpost: http://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
|
||||||
|
|
||||||
|
|
||||||
### Thanks:
|
### Thanks:
|
||||||
|
|
350
changelog.md
350
changelog.md
File diff suppressed because it is too large
Load diff
|
@ -96,6 +96,8 @@ Contributors:
|
||||||
* Alfred Wingate
|
* Alfred Wingate
|
||||||
* Zhanze Wang
|
* Zhanze Wang
|
||||||
* Houston Wong
|
* Houston Wong
|
||||||
|
* Mohamed Rezk
|
||||||
|
* Ryosuke Kazami
|
||||||
|
|
||||||
|
|
||||||
Created by:
|
Created by:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = '1.27.2'
|
__version__ = "1.28.0"
|
||||||
|
|
|
@ -3,6 +3,8 @@ from prompt_toolkit.enums import EditingMode
|
||||||
from prompt_toolkit.filters import completion_is_selected, emacs_mode
|
from prompt_toolkit.filters import completion_is_selected, emacs_mode
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
|
|
||||||
|
from .packages.toolkit.fzf import search_history
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,6 +103,12 @@ def mycli_bindings(mycli):
|
||||||
cursorpos_abs -= 1
|
cursorpos_abs -= 1
|
||||||
b.cursor_position = min(cursorpos_abs, len(b.text))
|
b.cursor_position = min(cursorpos_abs, len(b.text))
|
||||||
|
|
||||||
|
@kb.add('c-r', filter=emacs_mode)
|
||||||
|
def _(event):
|
||||||
|
"""Search history using fzf or default reverse incremental search."""
|
||||||
|
_logger.debug('Detected <C-r> key.')
|
||||||
|
search_history(event)
|
||||||
|
|
||||||
@kb.add('enter', filter=completion_is_selected)
|
@kb.add('enter', filter=completion_is_selected)
|
||||||
def _(event):
|
def _(event):
|
||||||
"""Makes the enter key work as the tab key only when showing the menu.
|
"""Makes the enter key work as the tab key only when showing the menu.
|
||||||
|
|
|
@ -36,7 +36,6 @@ from prompt_toolkit.formatted_text import ANSI
|
||||||
from prompt_toolkit.layout.processors import (HighlightMatchingBracketProcessor,
|
from prompt_toolkit.layout.processors import (HighlightMatchingBracketProcessor,
|
||||||
ConditionalProcessor)
|
ConditionalProcessor)
|
||||||
from prompt_toolkit.lexers import PygmentsLexer
|
from prompt_toolkit.lexers import PygmentsLexer
|
||||||
from prompt_toolkit.history import FileHistory
|
|
||||||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||||
|
|
||||||
from .packages.special.main import NO_QUERY
|
from .packages.special.main import NO_QUERY
|
||||||
|
@ -44,6 +43,7 @@ from .packages.prompt_utils import confirm, confirm_destructive_query
|
||||||
from .packages.tabular_output import sql_format
|
from .packages.tabular_output import sql_format
|
||||||
from .packages import special
|
from .packages import special
|
||||||
from .packages.special.favoritequeries import FavoriteQueries
|
from .packages.special.favoritequeries import FavoriteQueries
|
||||||
|
from .packages.toolkit.history import FileHistoryWithTimestamp
|
||||||
from .sqlcompleter import SQLCompleter
|
from .sqlcompleter import SQLCompleter
|
||||||
from .clitoolbar import create_toolbar_tokens_func
|
from .clitoolbar import create_toolbar_tokens_func
|
||||||
from .clistyle import style_factory, style_factory_output
|
from .clistyle import style_factory, style_factory_output
|
||||||
|
@ -626,7 +626,7 @@ class MyCli(object):
|
||||||
history_file = os.path.expanduser(
|
history_file = os.path.expanduser(
|
||||||
os.environ.get('MYCLI_HISTFILE', '~/.mycli-history'))
|
os.environ.get('MYCLI_HISTFILE', '~/.mycli-history'))
|
||||||
if dir_path_exists(history_file):
|
if dir_path_exists(history_file):
|
||||||
history = FileHistory(history_file)
|
history = FileHistoryWithTimestamp(history_file)
|
||||||
else:
|
else:
|
||||||
history = None
|
history = None
|
||||||
self.echo(
|
self.echo(
|
||||||
|
|
|
@ -138,6 +138,8 @@ def suggest_based_on_last_token(token, text_before_cursor, full_text, identifier
|
||||||
|
|
||||||
if not token:
|
if not token:
|
||||||
return [{'type': 'keyword'}, {'type': 'special'}]
|
return [{'type': 'keyword'}, {'type': 'special'}]
|
||||||
|
elif token_v == "*":
|
||||||
|
return [{'type': 'keyword'}]
|
||||||
elif token_v.endswith('('):
|
elif token_v.endswith('('):
|
||||||
p = sqlparse.parse(text_before_cursor)[0]
|
p = sqlparse.parse(text_before_cursor)[0]
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ def guess_socket_location():
|
||||||
for r, dirs, files in os.walk(directory, topdown=True):
|
for r, dirs, files in os.walk(directory, topdown=True):
|
||||||
for filename in files:
|
for filename in files:
|
||||||
name, ext = os.path.splitext(filename)
|
name, ext = os.path.splitext(filename)
|
||||||
if name.startswith("mysql") and ext in ('.socket', '.sock'):
|
if name.startswith("mysql") and name != "mysqlx" and ext in ('.socket', '.sock'):
|
||||||
return os.path.join(r, filename)
|
return os.path.join(r, filename)
|
||||||
dirs[:] = [d for d in dirs if d.startswith("mysql")]
|
dirs[:] = [d for d in dirs if d.startswith("mysql")]
|
||||||
return None
|
return None
|
||||||
|
|
0
mycli/packages/toolkit/__init__.py
Normal file
0
mycli/packages/toolkit/__init__.py
Normal file
45
mycli/packages/toolkit/fzf.py
Normal file
45
mycli/packages/toolkit/fzf.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
from shutil import which
|
||||||
|
|
||||||
|
from pyfzf import FzfPrompt
|
||||||
|
from prompt_toolkit import search
|
||||||
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
||||||
|
|
||||||
|
from .history import FileHistoryWithTimestamp
|
||||||
|
|
||||||
|
|
||||||
|
class Fzf(FzfPrompt):
|
||||||
|
def __init__(self):
|
||||||
|
self.executable = which("fzf")
|
||||||
|
if self.executable:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def is_available(self) -> bool:
|
||||||
|
return self.executable is not None
|
||||||
|
|
||||||
|
|
||||||
|
def search_history(event: KeyPressEvent):
|
||||||
|
buffer = event.current_buffer
|
||||||
|
history = buffer.history
|
||||||
|
|
||||||
|
fzf = Fzf()
|
||||||
|
|
||||||
|
if fzf.is_available() and isinstance(history, FileHistoryWithTimestamp):
|
||||||
|
history_items_with_timestamp = history.load_history_with_timestamp()
|
||||||
|
|
||||||
|
formatted_history_items = []
|
||||||
|
original_history_items = []
|
||||||
|
for item, timestamp in history_items_with_timestamp:
|
||||||
|
formatted_item = item.replace('\n', ' ')
|
||||||
|
timestamp = timestamp.split(".")[0] if "." in timestamp else timestamp
|
||||||
|
formatted_history_items.append(f"{timestamp} {formatted_item}")
|
||||||
|
original_history_items.append(item)
|
||||||
|
|
||||||
|
result = fzf.prompt(formatted_history_items, fzf_options="--tiebreak=index")
|
||||||
|
|
||||||
|
if result:
|
||||||
|
selected_index = formatted_history_items.index(result[0])
|
||||||
|
buffer.text = original_history_items[selected_index]
|
||||||
|
buffer.cursor_position = len(buffer.text)
|
||||||
|
else:
|
||||||
|
# Fallback to default reverse incremental search
|
||||||
|
search.start_search(direction=search.SearchDirection.BACKWARD)
|
52
mycli/packages/toolkit/history.py
Normal file
52
mycli/packages/toolkit/history.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import os
|
||||||
|
from typing import Iterable, Union, List, Tuple
|
||||||
|
|
||||||
|
from prompt_toolkit.history import FileHistory
|
||||||
|
|
||||||
|
_StrOrBytesPath = Union[str, bytes, os.PathLike]
|
||||||
|
|
||||||
|
|
||||||
|
class FileHistoryWithTimestamp(FileHistory):
|
||||||
|
"""
|
||||||
|
:class:`.FileHistory` class that stores all strings in a file with timestamp.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, filename: _StrOrBytesPath) -> None:
|
||||||
|
self.filename = filename
|
||||||
|
super().__init__(filename)
|
||||||
|
|
||||||
|
def load_history_with_timestamp(self) -> List[Tuple[str, str]]:
|
||||||
|
"""
|
||||||
|
Load history entries along with their timestamps.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Tuple[str, str]]: A list of tuples where each tuple contains
|
||||||
|
a history entry and its corresponding timestamp.
|
||||||
|
"""
|
||||||
|
history_with_timestamp: List[Tuple[str, str]] = []
|
||||||
|
lines: List[str] = []
|
||||||
|
timestamp: str = ""
|
||||||
|
|
||||||
|
def add() -> None:
|
||||||
|
if lines:
|
||||||
|
# Join and drop trailing newline.
|
||||||
|
string = "".join(lines)[:-1]
|
||||||
|
history_with_timestamp.append((string, timestamp))
|
||||||
|
|
||||||
|
if os.path.exists(self.filename):
|
||||||
|
with open(self.filename, "rb") as f:
|
||||||
|
for line_bytes in f:
|
||||||
|
line = line_bytes.decode("utf-8", errors="replace")
|
||||||
|
|
||||||
|
if line.startswith("#"):
|
||||||
|
# Extract timestamp
|
||||||
|
timestamp = line[2:].strip()
|
||||||
|
elif line.startswith("+"):
|
||||||
|
lines.append(line[1:])
|
||||||
|
else:
|
||||||
|
add()
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
add()
|
||||||
|
|
||||||
|
return list(reversed(history_with_timestamp))
|
|
@ -31,7 +31,7 @@ class SQLCompleter(Completer):
|
||||||
'PORT', 'PRIMARY', 'PRIVILEGES', 'PROCESSLIST', 'PURGE',
|
'PORT', 'PRIMARY', 'PRIVILEGES', 'PROCESSLIST', 'PURGE',
|
||||||
'REFERENCES', 'REGEXP', 'RENAME', 'REPAIR', 'RESET', 'REVOKE',
|
'REFERENCES', 'REGEXP', 'RENAME', 'REPAIR', 'RESET', 'REVOKE',
|
||||||
'RIGHT', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', 'SAVEPOINT',
|
'RIGHT', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', 'SAVEPOINT',
|
||||||
'SESSION', 'SET', 'SHARE', 'SHOW', 'SLAVE', 'SMALLINT', 'SMALLINT',
|
'SESSION', 'SET', 'SHARE', 'SHOW', 'SLAVE', 'SMALLINT',
|
||||||
'START', 'STOP', 'TABLE', 'THEN', 'TINYINT', 'TO', 'TRANSACTION',
|
'START', 'STOP', 'TABLE', 'THEN', 'TINYINT', 'TO', 'TRANSACTION',
|
||||||
'TRIGGER', 'TRUNCATE', 'UNION', 'UNIQUE', 'UNSIGNED', 'USE',
|
'TRIGGER', 'TRUNCATE', 'UNION', 'UNIQUE', 'UNSIGNED', 'USE',
|
||||||
'USER', 'USING', 'VALUES', 'VARCHAR', 'VIEW', 'WHEN', 'WITH'
|
'USER', 'USING', 'VALUES', 'VARCHAR', 'VIEW', 'WHEN', 'WITH'
|
||||||
|
@ -475,8 +475,6 @@ class SQLCompleter(Completer):
|
||||||
|
|
||||||
elif suggestion['type'] == 'keyword':
|
elif suggestion['type'] == 'keyword':
|
||||||
keywords = self.find_matches(word_before_cursor, self.keywords,
|
keywords = self.find_matches(word_before_cursor, self.keywords,
|
||||||
start_only=True,
|
|
||||||
fuzzy=False,
|
|
||||||
casing=self.keyword_casing)
|
casing=self.keyword_casing)
|
||||||
completions.extend(keywords)
|
completions.extend(keywords)
|
||||||
|
|
||||||
|
@ -513,8 +511,8 @@ class SQLCompleter(Completer):
|
||||||
completions.extend(queries)
|
completions.extend(queries)
|
||||||
elif suggestion['type'] == 'table_format':
|
elif suggestion['type'] == 'table_format':
|
||||||
formats = self.find_matches(word_before_cursor,
|
formats = self.find_matches(word_before_cursor,
|
||||||
self.table_formats,
|
self.table_formats)
|
||||||
start_only=True, fuzzy=False)
|
|
||||||
completions.extend(formats)
|
completions.extend(formats)
|
||||||
elif suggestion['type'] == 'file_name':
|
elif suggestion['type'] == 'file_name':
|
||||||
file_names = self.find_files(word_before_cursor)
|
file_names = self.find_files(word_before_cursor)
|
||||||
|
|
|
@ -14,4 +14,4 @@ pyperclip>=1.8.1
|
||||||
importlib_resources>=5.0.0
|
importlib_resources>=5.0.0
|
||||||
pyaes>=1.6.1
|
pyaes>=1.6.1
|
||||||
sqlglot>=5.1.3
|
sqlglot>=5.1.3
|
||||||
setuptools
|
setuptools<=71.1.0
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -29,7 +29,8 @@ install_requirements = [
|
||||||
'configobj >= 5.0.5',
|
'configobj >= 5.0.5',
|
||||||
'cli_helpers[styles] >= 2.2.1',
|
'cli_helpers[styles] >= 2.2.1',
|
||||||
'pyperclip >= 1.8.1',
|
'pyperclip >= 1.8.1',
|
||||||
'pyaes >= 1.6.1'
|
'pyaes >= 1.6.1',
|
||||||
|
'pyfzf >= 0.3.1',
|
||||||
]
|
]
|
||||||
|
|
||||||
if sys.version_info.minor < 9:
|
if sys.version_info.minor < 9:
|
||||||
|
|
|
@ -89,6 +89,9 @@ keyword_casing = auto
|
||||||
# disabled pager on startup
|
# disabled pager on startup
|
||||||
enable_pager = True
|
enable_pager = True
|
||||||
|
|
||||||
|
# Choose a specific pager
|
||||||
|
pager = less
|
||||||
|
|
||||||
# Custom colors for the completion menu, toolbar, etc.
|
# Custom colors for the completion menu, toolbar, etc.
|
||||||
[colors]
|
[colors]
|
||||||
completion-menu.completion.current = "bg:#ffffff #000000"
|
completion-menu.completion.current = "bg:#ffffff #000000"
|
||||||
|
|
|
@ -5,17 +5,17 @@ from prompt_toolkit.document import Document
|
||||||
import mycli.packages.special.main as special
|
import mycli.packages.special.main as special
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
'users': ['id', 'email', 'first_name', 'last_name'],
|
"users": ["id", "email", "first_name", "last_name"],
|
||||||
'orders': ['id', 'ordered_date', 'status'],
|
"orders": ["id", "ordered_date", "status"],
|
||||||
'select': ['id', 'insert', 'ABC'],
|
"select": ["id", "insert", "ABC"],
|
||||||
'réveillé': ['id', 'insert', 'ABC']
|
"réveillé": ["id", "insert", "ABC"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def completer():
|
def completer():
|
||||||
|
|
||||||
import mycli.sqlcompleter as sqlcompleter
|
import mycli.sqlcompleter as sqlcompleter
|
||||||
|
|
||||||
comp = sqlcompleter.SQLCompleter(smart_completion=True)
|
comp = sqlcompleter.SQLCompleter(smart_completion=True)
|
||||||
|
|
||||||
tables, columns = [], []
|
tables, columns = [], []
|
||||||
|
@ -24,10 +24,10 @@ def completer():
|
||||||
tables.append((table,))
|
tables.append((table,))
|
||||||
columns.extend([(table, col) for col in cols])
|
columns.extend([(table, col) for col in cols])
|
||||||
|
|
||||||
comp.set_dbname('test')
|
comp.set_dbname("test")
|
||||||
comp.extend_schemata('test')
|
comp.extend_schemata("test")
|
||||||
comp.extend_relations(tables, kind='tables')
|
comp.extend_relations(tables, kind="tables")
|
||||||
comp.extend_columns(columns, kind='tables')
|
comp.extend_columns(columns, kind="tables")
|
||||||
comp.extend_special_commands(special.COMMANDS)
|
comp.extend_special_commands(special.COMMANDS)
|
||||||
|
|
||||||
return comp
|
return comp
|
||||||
|
@ -36,59 +36,85 @@ def completer():
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def complete_event():
|
def complete_event():
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
return Mock()
|
return Mock()
|
||||||
|
|
||||||
|
|
||||||
def test_special_name_completion(completer, complete_event):
|
def test_special_name_completion(completer, complete_event):
|
||||||
text = '\\d'
|
text = "\\d"
|
||||||
position = len('\\d')
|
position = len("\\d")
|
||||||
result = completer.get_completions(
|
result = completer.get_completions(
|
||||||
Document(text=text, cursor_position=position),
|
Document(text=text, cursor_position=position), complete_event
|
||||||
complete_event)
|
)
|
||||||
assert result == [Completion(text='\\dt', start_position=-2)]
|
assert result == [Completion(text="\\dt", start_position=-2)]
|
||||||
|
|
||||||
|
|
||||||
def test_empty_string_completion(completer, complete_event):
|
def test_empty_string_completion(completer, complete_event):
|
||||||
text = ''
|
text = ""
|
||||||
position = 0
|
position = 0
|
||||||
result = list(
|
result = list(
|
||||||
completer.get_completions(
|
completer.get_completions(
|
||||||
Document(text=text, cursor_position=position),
|
Document(text=text, cursor_position=position), complete_event
|
||||||
complete_event))
|
)
|
||||||
assert list(map(Completion, completer.keywords +
|
)
|
||||||
completer.special_commands)) == result
|
assert (
|
||||||
|
list(map(Completion, completer.keywords + completer.special_commands)) == result
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_select_keyword_completion(completer, complete_event):
|
def test_select_keyword_completion(completer, complete_event):
|
||||||
text = 'SEL'
|
text = "SEL"
|
||||||
position = len('SEL')
|
position = len("SEL")
|
||||||
result = completer.get_completions(
|
result = completer.get_completions(
|
||||||
Document(text=text, cursor_position=position),
|
Document(text=text, cursor_position=position), complete_event
|
||||||
complete_event)
|
)
|
||||||
assert list(result) == list([Completion(text='SELECT', start_position=-3)])
|
assert list(result) == list([Completion(text="SELECT", start_position=-3)])
|
||||||
|
|
||||||
|
|
||||||
|
def test_select_star(completer, complete_event):
|
||||||
|
text = "SELECT * "
|
||||||
|
position = len(text)
|
||||||
|
result = completer.get_completions(
|
||||||
|
Document(text=text, cursor_position=position), complete_event
|
||||||
|
)
|
||||||
|
assert list(result) == list(map(Completion, completer.keywords))
|
||||||
|
|
||||||
|
|
||||||
def test_table_completion(completer, complete_event):
|
def test_table_completion(completer, complete_event):
|
||||||
text = 'SELECT * FROM '
|
text = "SELECT * FROM "
|
||||||
position = len(text)
|
position = len(text)
|
||||||
result = completer.get_completions(
|
result = completer.get_completions(
|
||||||
Document(text=text, cursor_position=position), complete_event)
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert list(result) == list([
|
)
|
||||||
Completion(text='users', start_position=0),
|
assert list(result) == list(
|
||||||
Completion(text='orders', start_position=0),
|
[
|
||||||
Completion(text='`select`', start_position=0),
|
Completion(text="users", start_position=0),
|
||||||
Completion(text='`réveillé`', start_position=0),
|
Completion(text="orders", start_position=0),
|
||||||
])
|
Completion(text="`select`", start_position=0),
|
||||||
|
Completion(text="`réveillé`", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_function_name_completion(completer, complete_event):
|
def test_function_name_completion(completer, complete_event):
|
||||||
text = 'SELECT MA'
|
text = "SELECT MA"
|
||||||
position = len('SELECT MA')
|
position = len("SELECT MA")
|
||||||
result = completer.get_completions(
|
result = completer.get_completions(
|
||||||
Document(text=text, cursor_position=position), complete_event)
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert list(result) == list([Completion(text='MAX', start_position=-2),
|
)
|
||||||
Completion(text='MASTER', start_position=-2),
|
assert list(result) == list(
|
||||||
])
|
[
|
||||||
|
Completion(text="MAX", start_position=-2),
|
||||||
|
Completion(text="CHANGE MASTER TO", start_position=-2),
|
||||||
|
Completion(text="CURRENT_TIMESTAMP", start_position=-2),
|
||||||
|
Completion(text="DECIMAL", start_position=-2),
|
||||||
|
Completion(text="FORMAT", start_position=-2),
|
||||||
|
Completion(text="MASTER", start_position=-2),
|
||||||
|
Completion(text="PRIMARY", start_position=-2),
|
||||||
|
Completion(text="ROW_FORMAT", start_position=-2),
|
||||||
|
Completion(text="SMALLINT", start_position=-2),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_column_names(completer, complete_event):
|
def test_suggested_column_names(completer, complete_event):
|
||||||
|
@ -99,21 +125,25 @@ def test_suggested_column_names(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT from users'
|
text = "SELECT from users"
|
||||||
position = len('SELECT ')
|
position = len("SELECT ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='email', start_position=0),
|
[
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='last_name', start_position=0),
|
Completion(text="id", start_position=0),
|
||||||
] +
|
Completion(text="email", start_position=0),
|
||||||
list(map(Completion, completer.functions)) +
|
Completion(text="first_name", start_position=0),
|
||||||
[Completion(text='users', start_position=0)] +
|
Completion(text="last_name", start_position=0),
|
||||||
list(map(Completion, completer.keywords)))
|
]
|
||||||
|
+ list(map(Completion, completer.functions))
|
||||||
|
+ [Completion(text="users", start_position=0)]
|
||||||
|
+ list(map(Completion, completer.keywords))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_column_names_in_function(completer, complete_event):
|
def test_suggested_column_names_in_function(completer, complete_event):
|
||||||
|
@ -125,17 +155,20 @@ def test_suggested_column_names_in_function(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT MAX( from users'
|
text = "SELECT MAX( from users"
|
||||||
position = len('SELECT MAX(')
|
position = len("SELECT MAX(")
|
||||||
result = completer.get_completions(
|
result = completer.get_completions(
|
||||||
Document(text=text, cursor_position=position),
|
Document(text=text, cursor_position=position), complete_event
|
||||||
complete_event)
|
)
|
||||||
assert list(result) == list([
|
assert list(result) == list(
|
||||||
Completion(text='*', start_position=0),
|
[
|
||||||
Completion(text='id', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='email', start_position=0),
|
Completion(text="id", start_position=0),
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="email", start_position=0),
|
||||||
Completion(text='last_name', start_position=0)])
|
Completion(text="first_name", start_position=0),
|
||||||
|
Completion(text="last_name", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_column_names_with_table_dot(completer, complete_event):
|
def test_suggested_column_names_with_table_dot(completer, complete_event):
|
||||||
|
@ -146,17 +179,22 @@ def test_suggested_column_names_with_table_dot(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT users. from users'
|
text = "SELECT users. from users"
|
||||||
position = len('SELECT users.')
|
position = len("SELECT users.")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='email', start_position=0),
|
[
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='last_name', start_position=0)])
|
Completion(text="id", start_position=0),
|
||||||
|
Completion(text="email", start_position=0),
|
||||||
|
Completion(text="first_name", start_position=0),
|
||||||
|
Completion(text="last_name", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_column_names_with_alias(completer, complete_event):
|
def test_suggested_column_names_with_alias(completer, complete_event):
|
||||||
|
@ -167,17 +205,22 @@ def test_suggested_column_names_with_alias(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT u. from users u'
|
text = "SELECT u. from users u"
|
||||||
position = len('SELECT u.')
|
position = len("SELECT u.")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='email', start_position=0),
|
[
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='last_name', start_position=0)])
|
Completion(text="id", start_position=0),
|
||||||
|
Completion(text="email", start_position=0),
|
||||||
|
Completion(text="first_name", start_position=0),
|
||||||
|
Completion(text="last_name", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_multiple_column_names(completer, complete_event):
|
def test_suggested_multiple_column_names(completer, complete_event):
|
||||||
|
@ -189,20 +232,25 @@ def test_suggested_multiple_column_names(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT id, from users u'
|
text = "SELECT id, from users u"
|
||||||
position = len('SELECT id, ')
|
position = len("SELECT id, ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='email', start_position=0),
|
[
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='last_name', start_position=0)] +
|
Completion(text="id", start_position=0),
|
||||||
list(map(Completion, completer.functions)) +
|
Completion(text="email", start_position=0),
|
||||||
[Completion(text='u', start_position=0)] +
|
Completion(text="first_name", start_position=0),
|
||||||
list(map(Completion, completer.keywords)))
|
Completion(text="last_name", start_position=0),
|
||||||
|
]
|
||||||
|
+ list(map(Completion, completer.functions))
|
||||||
|
+ [Completion(text="u", start_position=0)]
|
||||||
|
+ list(map(Completion, completer.keywords))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_multiple_column_names_with_alias(completer, complete_event):
|
def test_suggested_multiple_column_names_with_alias(completer, complete_event):
|
||||||
|
@ -214,17 +262,22 @@ def test_suggested_multiple_column_names_with_alias(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT u.id, u. from users u'
|
text = "SELECT u.id, u. from users u"
|
||||||
position = len('SELECT u.id, u.')
|
position = len("SELECT u.id, u.")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='email', start_position=0),
|
[
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='last_name', start_position=0)])
|
Completion(text="id", start_position=0),
|
||||||
|
Completion(text="email", start_position=0),
|
||||||
|
Completion(text="first_name", start_position=0),
|
||||||
|
Completion(text="last_name", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_multiple_column_names_with_dot(completer, complete_event):
|
def test_suggested_multiple_column_names_with_dot(completer, complete_event):
|
||||||
|
@ -236,154 +289,185 @@ def test_suggested_multiple_column_names_with_dot(completer, complete_event):
|
||||||
:return:
|
:return:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = 'SELECT users.id, users. from users u'
|
text = "SELECT users.id, users. from users u"
|
||||||
position = len('SELECT users.id, users.')
|
position = len("SELECT users.id, users.")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='email', start_position=0),
|
[
|
||||||
Completion(text='first_name', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='last_name', start_position=0)])
|
Completion(text="id", start_position=0),
|
||||||
|
Completion(text="email", start_position=0),
|
||||||
|
Completion(text="first_name", start_position=0),
|
||||||
|
Completion(text="last_name", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_aliases_after_on(completer, complete_event):
|
def test_suggested_aliases_after_on(completer, complete_event):
|
||||||
text = 'SELECT u.name, o.id FROM users u JOIN orders o ON '
|
text = "SELECT u.name, o.id FROM users u JOIN orders o ON "
|
||||||
position = len('SELECT u.name, o.id FROM users u JOIN orders o ON ')
|
position = len("SELECT u.name, o.id FROM users u JOIN orders o ON ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='u', start_position=0),
|
)
|
||||||
Completion(text='o', start_position=0),
|
assert result == list(
|
||||||
])
|
[
|
||||||
|
Completion(text="u", start_position=0),
|
||||||
|
Completion(text="o", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_aliases_after_on_right_side(completer, complete_event):
|
def test_suggested_aliases_after_on_right_side(completer, complete_event):
|
||||||
text = 'SELECT u.name, o.id FROM users u JOIN orders o ON o.user_id = '
|
text = "SELECT u.name, o.id FROM users u JOIN orders o ON o.user_id = "
|
||||||
position = len(
|
position = len("SELECT u.name, o.id FROM users u JOIN orders o ON o.user_id = ")
|
||||||
'SELECT u.name, o.id FROM users u JOIN orders o ON o.user_id = ')
|
result = list(
|
||||||
result = list(completer.get_completions(
|
completer.get_completions(
|
||||||
Document(text=text, cursor_position=position),
|
Document(text=text, cursor_position=position), complete_event
|
||||||
complete_event))
|
)
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='u', start_position=0),
|
assert result == list(
|
||||||
Completion(text='o', start_position=0),
|
[
|
||||||
])
|
Completion(text="u", start_position=0),
|
||||||
|
Completion(text="o", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_tables_after_on(completer, complete_event):
|
def test_suggested_tables_after_on(completer, complete_event):
|
||||||
text = 'SELECT users.name, orders.id FROM users JOIN orders ON '
|
text = "SELECT users.name, orders.id FROM users JOIN orders ON "
|
||||||
position = len('SELECT users.name, orders.id FROM users JOIN orders ON ')
|
position = len("SELECT users.name, orders.id FROM users JOIN orders ON ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='users', start_position=0),
|
)
|
||||||
Completion(text='orders', start_position=0),
|
assert result == list(
|
||||||
])
|
[
|
||||||
|
Completion(text="users", start_position=0),
|
||||||
|
Completion(text="orders", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_suggested_tables_after_on_right_side(completer, complete_event):
|
def test_suggested_tables_after_on_right_side(completer, complete_event):
|
||||||
text = 'SELECT users.name, orders.id FROM users JOIN orders ON orders.user_id = '
|
text = "SELECT users.name, orders.id FROM users JOIN orders ON orders.user_id = "
|
||||||
position = len(
|
position = len(
|
||||||
'SELECT users.name, orders.id FROM users JOIN orders ON orders.user_id = ')
|
"SELECT users.name, orders.id FROM users JOIN orders ON orders.user_id = "
|
||||||
result = list(completer.get_completions(
|
)
|
||||||
Document(text=text, cursor_position=position),
|
result = list(
|
||||||
complete_event))
|
completer.get_completions(
|
||||||
assert result == list([
|
Document(text=text, cursor_position=position), complete_event
|
||||||
Completion(text='users', start_position=0),
|
)
|
||||||
Completion(text='orders', start_position=0),
|
)
|
||||||
])
|
assert result == list(
|
||||||
|
[
|
||||||
|
Completion(text="users", start_position=0),
|
||||||
|
Completion(text="orders", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_table_names_after_from(completer, complete_event):
|
def test_table_names_after_from(completer, complete_event):
|
||||||
text = 'SELECT * FROM '
|
text = "SELECT * FROM "
|
||||||
position = len('SELECT * FROM ')
|
position = len("SELECT * FROM ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='users', start_position=0),
|
)
|
||||||
Completion(text='orders', start_position=0),
|
assert result == list(
|
||||||
Completion(text='`select`', start_position=0),
|
[
|
||||||
Completion(text='`réveillé`', start_position=0),
|
Completion(text="users", start_position=0),
|
||||||
])
|
Completion(text="orders", start_position=0),
|
||||||
|
Completion(text="`select`", start_position=0),
|
||||||
|
Completion(text="`réveillé`", start_position=0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_auto_escaped_col_names(completer, complete_event):
|
def test_auto_escaped_col_names(completer, complete_event):
|
||||||
text = 'SELECT from `select`'
|
text = "SELECT from `select`"
|
||||||
position = len('SELECT ')
|
position = len("SELECT ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
|
)
|
||||||
|
)
|
||||||
assert result == [
|
assert result == [
|
||||||
Completion(text='*', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
Completion(text='id', start_position=0),
|
Completion(text="id", start_position=0),
|
||||||
Completion(text='`insert`', start_position=0),
|
Completion(text="`insert`", start_position=0),
|
||||||
Completion(text='`ABC`', start_position=0),
|
Completion(text="`ABC`", start_position=0),
|
||||||
] + \
|
] + list(map(Completion, completer.functions)) + [
|
||||||
list(map(Completion, completer.functions)) + \
|
Completion(text="select", start_position=0)
|
||||||
[Completion(text='select', start_position=0)] + \
|
] + list(map(Completion, completer.keywords))
|
||||||
list(map(Completion, completer.keywords))
|
|
||||||
|
|
||||||
|
|
||||||
def test_un_escaped_table_names(completer, complete_event):
|
def test_un_escaped_table_names(completer, complete_event):
|
||||||
text = 'SELECT from réveillé'
|
text = "SELECT from réveillé"
|
||||||
position = len('SELECT ')
|
position = len("SELECT ")
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
assert result == list([
|
)
|
||||||
Completion(text='*', start_position=0),
|
)
|
||||||
Completion(text='id', start_position=0),
|
assert result == list(
|
||||||
Completion(text='`insert`', start_position=0),
|
[
|
||||||
Completion(text='`ABC`', start_position=0),
|
Completion(text="*", start_position=0),
|
||||||
] +
|
Completion(text="id", start_position=0),
|
||||||
list(map(Completion, completer.functions)) +
|
Completion(text="`insert`", start_position=0),
|
||||||
[Completion(text='réveillé', start_position=0)] +
|
Completion(text="`ABC`", start_position=0),
|
||||||
list(map(Completion, completer.keywords)))
|
]
|
||||||
|
+ list(map(Completion, completer.functions))
|
||||||
|
+ [Completion(text="réveillé", start_position=0)]
|
||||||
|
+ list(map(Completion, completer.keywords))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def dummy_list_path(dir_name):
|
def dummy_list_path(dir_name):
|
||||||
dirs = {
|
dirs = {
|
||||||
'/': [
|
"/": [
|
||||||
'dir1',
|
"dir1",
|
||||||
'file1.sql',
|
"file1.sql",
|
||||||
'file2.sql',
|
"file2.sql",
|
||||||
],
|
],
|
||||||
'/dir1': [
|
"/dir1": [
|
||||||
'subdir1',
|
"subdir1",
|
||||||
'subfile1.sql',
|
"subfile1.sql",
|
||||||
'subfile2.sql',
|
"subfile2.sql",
|
||||||
],
|
],
|
||||||
'/dir1/subdir1': [
|
"/dir1/subdir1": [
|
||||||
'lastfile.sql',
|
"lastfile.sql",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
return dirs.get(dir_name, [])
|
return dirs.get(dir_name, [])
|
||||||
|
|
||||||
|
|
||||||
@patch('mycli.packages.filepaths.list_path', new=dummy_list_path)
|
@patch("mycli.packages.filepaths.list_path", new=dummy_list_path)
|
||||||
@pytest.mark.parametrize('text,expected', [
|
@pytest.mark.parametrize(
|
||||||
# ('source ', [('~', 0),
|
"text,expected",
|
||||||
# ('/', 0),
|
[
|
||||||
# ('.', 0),
|
# ('source ', [('~', 0),
|
||||||
# ('..', 0)]),
|
# ('/', 0),
|
||||||
('source /', [('dir1', 0),
|
# ('.', 0),
|
||||||
('file1.sql', 0),
|
# ('..', 0)]),
|
||||||
('file2.sql', 0)]),
|
("source /", [("dir1", 0), ("file1.sql", 0), ("file2.sql", 0)]),
|
||||||
('source /dir1/', [('subdir1', 0),
|
("source /dir1/", [("subdir1", 0), ("subfile1.sql", 0), ("subfile2.sql", 0)]),
|
||||||
('subfile1.sql', 0),
|
("source /dir1/subdir1/", [("lastfile.sql", 0)]),
|
||||||
('subfile2.sql', 0)]),
|
],
|
||||||
('source /dir1/subdir1/', [('lastfile.sql', 0)]),
|
)
|
||||||
])
|
|
||||||
def test_file_name_completion(completer, complete_event, text, expected):
|
def test_file_name_completion(completer, complete_event, text, expected):
|
||||||
position = len(text)
|
position = len(text)
|
||||||
result = list(completer.get_completions(
|
result = list(
|
||||||
Document(text=text, cursor_position=position),
|
completer.get_completions(
|
||||||
complete_event))
|
Document(text=text, cursor_position=position), complete_event
|
||||||
|
)
|
||||||
|
)
|
||||||
expected = list((Completion(txt, pos) for txt, pos in expected))
|
expected = list((Completion(txt, pos) for txt, pos in expected))
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
Loading…
Add table
Reference in a new issue