Skip to content

Commit f9a4d94

Browse files
authored
Merge pull request #917 from rolandwalker/clip-special-command
Add \clip special command
2 parents 5635e84 + 9e902a0 commit f9a4d94

File tree

6 files changed

+75
-0
lines changed

6 files changed

+75
-0
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ Features:
66

77
* Add an option `--init-command` to execute SQL after connecting (Thanks: [KITAGAWA Yasutaka]).
88
* Use InputMode.REPLACE_SINGLE
9+
* Add a `\clip` special command to copy queries to the system clipboard.
910
* Add a special command `\pipe_once` to pipe output to a subprocess.
1011

12+
1113
Bug Fixes:
1214
----------
1315
* Fixed compatibility with sqlparse 0.4 (Thanks: [mtorromeo]).

mycli/main.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,24 @@ def handle_editor_command(self, text):
511511
continue
512512
return text
513513

514+
def handle_clip_command(self, text):
515+
"""A clip command is any query that is prefixed or suffixed by a
516+
'\clip'.
517+
518+
:param text: Document
519+
:return: Boolean
520+
521+
"""
522+
523+
if special.clip_command(text):
524+
query = (special.get_clip_query(text) or
525+
self.get_last_query())
526+
message = special.copy_query_to_clipboard(sql=query)
527+
if message:
528+
raise RuntimeError(message)
529+
return True
530+
return False
531+
514532
def run_cli(self):
515533
iterations = 0
516534
sqlexecute = self.sqlexecute
@@ -582,6 +600,15 @@ def one_iteration(text=None):
582600
self.echo(str(e), err=True, fg='red')
583601
return
584602

603+
try:
604+
if self.handle_clip_command(text):
605+
return
606+
except RuntimeError as e:
607+
logger.error("sql: %r, error: %r", text, e)
608+
logger.error("traceback: %r", traceback.format_exc())
609+
self.echo(str(e), err=True, fg='red')
610+
return
611+
585612
if not text.strip():
586613
return
587614

mycli/packages/special/iocommands.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from time import sleep
99

1010
import click
11+
import pyperclip
1112
import sqlparse
1213

1314
from . import export
@@ -161,6 +162,47 @@ def open_external_editor(filename=None, sql=None):
161162
return (query, message)
162163

163164

165+
@export
166+
def clip_command(command):
167+
"""Is this a clip command?
168+
169+
:param command: string
170+
171+
"""
172+
# It is possible to have `\clip` or `SELECT * FROM \clip`. So we check
173+
# for both conditions.
174+
return command.strip().endswith('\\clip') or command.strip().startswith('\\clip')
175+
176+
177+
@export
178+
def get_clip_query(sql):
179+
"""Get the query part of a clip command."""
180+
sql = sql.strip()
181+
182+
# The reason we can't simply do .strip('\clip') is that it strips characters,
183+
# not a substring. So it'll strip "c" in the end of the sql also!
184+
pattern = re.compile('(^\\\clip|\\\clip$)')
185+
while pattern.search(sql):
186+
sql = pattern.sub('', sql)
187+
188+
return sql
189+
190+
191+
@export
192+
def copy_query_to_clipboard(sql=None):
193+
"""Send query to the clipboard."""
194+
195+
sql = sql or ''
196+
message = None
197+
198+
try:
199+
pyperclip.copy(u'{sql}'.format(sql=sql))
200+
except RuntimeError as e:
201+
message = 'Error clipping query: %s.' % e.strerror
202+
203+
return message
204+
205+
164206
@special_command('\\f', '\\f [name [args..]]', 'List or execute favorite queries.', arg_type=PARSED_QUERY, case_sensitive=True)
165207
def execute_favorite_query(cur, arg, **_):
166208
"""Returns (title, rows, headers, status)"""

mycli/packages/special/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def quit(*_args):
112112

113113
@special_command('\\e', '\\e', 'Edit command with editor (uses $EDITOR).',
114114
arg_type=NO_QUERY, case_sensitive=True)
115+
@special_command('\\clip', '\\clip', 'Copy query to the system clipboard.',
116+
arg_type=NO_QUERY, case_sensitive=True)
115117
@special_command('\\G', '\\G', 'Display current query results vertically.',
116118
arg_type=NO_QUERY, case_sensitive=True)
117119
def stub():

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
'configobj >= 5.0.5',
2626
'cryptography >= 1.0.0',
2727
'cli_helpers[styles] >= 2.0.1',
28+
'pyperclip >= 1.8.1'
2829
]
2930

3031

test/features/fixture_data/help_commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
| Command | Shortcut | Description |
33
+-------------+----------------------------+------------------------------------------------------------+
44
| \G | \G | Display current query results vertically. |
5+
| \clip | \clip | Copy query to the system clipboard. |
56
| \dt | \dt[+] [table] | List or describe tables. |
67
| \e | \e | Edit command with editor (uses $EDITOR). |
78
| \f | \f [name [args..]] | List or execute favorite queries. |

0 commit comments

Comments
 (0)