Skip to content

Commit

Permalink
Merge pull request #199 from dbcli/fix-prompt
Browse files Browse the repository at this point in the history
Fix windows path shown in prompt.
  • Loading branch information
amjith authored Jan 23, 2025
2 parents 9434b24 + 2965476 commit 4ddffce
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
- This adds a new `\llm` special command
- eg: `\llm "Who is the largest customer based on revenue?"`

### Bug Fixes

* Fix the windows path shown in prompt to remove escaping.

### Internal

* Change min required python version to 3.9+
Expand Down
82 changes: 46 additions & 36 deletions litecli/main.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,50 @@
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import print_function, unicode_literals

import itertools
import logging
import os
import re
import shutil
import sys
import traceback
import logging
import threading
from time import time
import traceback
from collections import namedtuple
from datetime import datetime
from io import open
from collections import namedtuple
from sqlite3 import OperationalError, sqlite_version
import shutil
from time import time

from cli_helpers.tabular_output import TabularOutputFormatter
from cli_helpers.tabular_output import preprocessors
import click
import sqlparse
from cli_helpers.tabular_output import TabularOutputFormatter, preprocessors
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import DynamicCompleter
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
from prompt_toolkit.shortcuts import PromptSession, CompleteStyle
from prompt_toolkit.document import Document
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
from prompt_toolkit.filters import HasFocus, IsDone
from prompt_toolkit.formatted_text import ANSI
from prompt_toolkit.history import FileHistory
from prompt_toolkit.layout.processors import (
HighlightMatchingBracketProcessor,
ConditionalProcessor,
HighlightMatchingBracketProcessor,
)
from prompt_toolkit.lexers import PygmentsLexer
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.shortcuts import CompleteStyle, PromptSession

from .packages.special.main import NO_QUERY
from .packages.prompt_utils import confirm, confirm_destructive_query
from .packages import special
from .sqlcompleter import SQLCompleter
from .clitoolbar import create_toolbar_tokens_func
from .clistyle import style_factory, style_factory_output
from .sqlexecute import SQLExecute
from .__init__ import __version__
from .clibuffer import cli_is_multiline
from .clistyle import style_factory, style_factory_output
from .clitoolbar import create_toolbar_tokens_func
from .completion_refresher import CompletionRefresher
from .config import config_location, ensure_dir_exists, get_config
from .key_bindings import cli_bindings
from .lexer import LiteCliLexer
from .__init__ import __version__
from .packages import special
from .packages.filepaths import dir_path_exists

import itertools
from .packages.prompt_utils import confirm, confirm_destructive_query
from .packages.special.main import NO_QUERY
from .sqlcompleter import SQLCompleter
from .sqlexecute import SQLExecute

click.disable_unicode_literals_warning = True

Expand Down Expand Up @@ -758,20 +756,32 @@ def get_completions(self, text, cursor_positition):
return self.completer.get_completions(Document(text=text, cursor_position=cursor_positition), None)

def get_prompt(self, string):
self.logger.debug("Getting prompt")
self.logger.debug("Getting prompt %r", string)
sqlexecute = self.sqlexecute
now = datetime.now()
string = string.replace("\\d", sqlexecute.dbname or "(none)")
string = string.replace("\\f", os.path.basename(sqlexecute.dbname or "(none)"))
string = string.replace("\\n", "\n")
string = string.replace("\\D", now.strftime("%a %b %d %H:%M:%S %Y"))
string = string.replace("\\m", now.strftime("%M"))
string = string.replace("\\P", now.strftime("%p"))
string = string.replace("\\R", now.strftime("%H"))
string = string.replace("\\r", now.strftime("%I"))
string = string.replace("\\s", now.strftime("%S"))
string = string.replace("\\_", " ")
return string

# Prepare the replacements dictionary
replacements = {
r"\d": sqlexecute.dbname or "(none)",
r"\f": os.path.basename(sqlexecute.dbname or "(none)"),
r"\n": "\n",
r"\D": now.strftime("%a %b %d %H:%M:%S %Y"),
r"\m": now.strftime("%M"),
r"\P": now.strftime("%p"),
r"\R": now.strftime("%H"),
r"\r": now.strftime("%I"),
r"\s": now.strftime("%S"),
r"\_": " ",
}
# Compile a regex pattern that matches any of the keys in replacements
pattern = re.compile("|".join(re.escape(key) for key in replacements.keys()))

# Define the replacement function
def replacer(match):
return replacements[match.group(0)]

# Perform the substitution
return pattern.sub(replacer, string)

def run_query(self, query, new_line=True):
"""Runs *query*."""
Expand Down
63 changes: 63 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from collections import namedtuple
from textwrap import dedent
import shutil
from datetime import datetime
from unittest.mock import patch

import click
from click.testing import CliRunner
Expand Down Expand Up @@ -267,3 +269,64 @@ def test_startup_commands(executor):
]

# implement tests on executions of the startupcommands


@patch("litecli.main.datetime") # Adjust if your module path is different
def test_get_prompt(mock_datetime):
# We'll freeze time at 2025-01-20 13:37:42 for comedic effect.
# Because "leet" times call for 13:37!
frozen_time = datetime(2025, 1, 20, 13, 37, 42)
mock_datetime.now.return_value = frozen_time
# Ensure `datetime` class is still accessible for strftime usage
mock_datetime.datetime = datetime

# Instantiate and connect
lc = LiteCli()
lc.connect("/tmp/litecli_test.db")

# 1. Test \d => full path to the DB
assert lc.get_prompt(r"\d") == "/tmp/litecli_test.db"

# 2. Test \f => basename of the DB
# (because "f" stands for "filename", presumably!)
assert lc.get_prompt(r"\f") == "litecli_test.db"

# 3. Test \_ => single space
assert lc.get_prompt(r"Hello\_World") == "Hello World"

# 4. Test \n => newline
# Just to be sure we're only inserting a newline,
# we can check length or assert the presence of "\n".
expected = f"Line1{os.linesep}Line2"
assert lc.get_prompt(r"Line1\nLine2") == expected

# 5. Test date/time placeholders (with frozen time):
# \D => e.g. 'Mon Jan 20 13:37:42 2025'
expected_date_str = frozen_time.strftime("%a %b %d %H:%M:%S %Y")
assert lc.get_prompt(r"\D") == expected_date_str

# 6. Test \m => minutes
assert lc.get_prompt(r"\m") == "37"

# 7. Test \P => AM/PM
# 13:37 is PM
assert lc.get_prompt(r"\P") == "PM"

# 8. Test \R => 24-hour format hour
assert lc.get_prompt(r"\R") == "13"

# 9. Test \r => 12-hour format hour
# 13:37 is 01 in 12-hour format
assert lc.get_prompt(r"\r") == "01"

# 10. Test \s => seconds
assert lc.get_prompt(r"\s") == "42"

# 11. Test when dbname is None => (none)
lc.connect(None) # Simulate no DB connection
assert lc.get_prompt(r"\d") == "(none)"
assert lc.get_prompt(r"\f") == "(none)"

# 12. Windows path
lc.connect("C:\\Users\\litecli\\litecli_test.db")
assert lc.get_prompt(r"\d") == "C:\\Users\\litecli\\litecli_test.db"

0 comments on commit 4ddffce

Please sign in to comment.