Enhanced version of the default Sublime Text paste_from_history.py plugin.
# | |
# My enhanced version of the default ST paste_from_history.py plugin. | |
# | |
# Alterations to do the following: | |
# | |
# 1) Select the paste to text from an overlay instead of a pop-up menu. | |
# 2) Allow operation from widgets, i.e. console and input panel. | |
# 3) Ensure that text copied or cut with my various copy_and_cut_text | |
# plugins gets added to the ClipboardHistory storage list. | |
# 4) is_enabled() replaced by a "clipboard history empty" status message. | |
# 5) Changes made to the display text, including the addition of a line | |
# count (if >1), an increase of the display length, and far superior | |
# display name formatting and efficiency for large clipboards. | |
# | |
# Replace paste_from_history command with this enhanced version. | |
# {"keys": ["ctrl+k", "ctrl+v"], "command": "paste_from_history_enhanced"} | |
# | |
import re | |
import sublime | |
import sublime_plugin | |
class ClipboardHistory(): | |
""" Stores the current clipboard history. """ | |
def __init__(self): | |
self.storage = [] | |
def push_text(self, text): | |
LIST_LIMIT = 15 | |
if not text: | |
return | |
display_text = self.build_display_text(text) | |
self.delete_duplicates(text) | |
self.storage.insert(0, (display_text, text)) | |
if len(self.storage) > LIST_LIMIT: | |
del self.storage[LIST_LIMIT:] | |
def build_display_text(self, text): | |
DOTS = "..." | |
DISPLAY_LENGTH = 70 | |
REDUCE_LENGTH_THRESHOLD = 1024 | |
# A threshold is used to avoid poor performance caused by | |
# a regex substitution on v. large (multi MB) clipboards. | |
if len(text) <= REDUCE_LENGTH_THRESHOLD: | |
display_text = text | |
else: | |
num_significant_chars = 0 | |
for cut_pos, char in enumerate(text, start=1): | |
if not char.isspace(): | |
num_significant_chars += 1 | |
if num_significant_chars > DISPLAY_LENGTH: | |
break | |
display_text = text[:cut_pos] | |
# Keep only significant chars, neatly space delimited. | |
# Like isspace(), '\s' matches ALL Unicode whitespace. | |
display_text = re.sub(r"\s+", " ", display_text) | |
display_text = display_text.strip() | |
num_lines = text.count("\n") + 1 | |
if num_lines == 1: | |
if len(display_text) > DISPLAY_LENGTH: | |
cut_pos = DISPLAY_LENGTH - len(DOTS) | |
display_text = display_text[:cut_pos] + DOTS | |
elif num_lines > 1: | |
lines_str = " [{} lines]".format(num_lines) | |
if len(display_text) + len(lines_str) <= DISPLAY_LENGTH: | |
display_text = display_text + lines_str | |
else: | |
cut_pos = DISPLAY_LENGTH - len(lines_str) - len(DOTS) | |
display_text = display_text[:cut_pos] + DOTS + lines_str | |
return display_text | |
def get(self): | |
return self.storage | |
def delete_duplicates(self, text): | |
self.storage = [s for s in self.storage if s[1] != text] | |
def empty(self): | |
return len(self.storage) == 0 | |
g_clipboard_history = ClipboardHistory() | |
class ClipboardHistoryUpdater(sublime_plugin.EventListener): | |
""" | |
Listens for ST command events and adds the clipboard's content to | |
the clipboard history when a clipboard altering command is run. | |
""" | |
def on_post_text_command(self, view, command_name, args): | |
# Include my copy_and_cut_text plugins so that text | |
# cut or copied with them gets added to the history. | |
clipboard_commands = ["copy", "cut", | |
"copy_and_cut_text_copy_line", | |
"copy_and_cut_text_copy_file", | |
"copy_and_cut_text_cut_line", | |
"copy_and_cut_text_cut_file", | |
"copy_and_cut_text_copy_current_word"] | |
if command_name in clipboard_commands: | |
g_clipboard_history.push_text(sublime.get_clipboard()) | |
class PasteFromHistoryEnhancedCommand(sublime_plugin.TextCommand): | |
""" | |
Displays the clipboard history in an overlay and pastes the selected item. | |
""" | |
def run(self, edit): | |
if g_clipboard_history.empty(): | |
message = "The clipboard history is empty" | |
self.view.window().status_message(message) | |
return | |
paste_list = g_clipboard_history.get() | |
items = [item[0] for item in paste_list] | |
self.view.window().show_quick_panel(items, self.on_done, | |
sublime.MONOSPACE_FONT) | |
def on_done(self, index): | |
if index == -1: | |
return | |
paste_text = g_clipboard_history.get()[index][1] | |
# Move paste_text to the top of history. | |
g_clipboard_history.push_text(paste_text) | |
sublime.set_clipboard(paste_text) | |
self.view.run_command("paste") | |
# If necessary restore the focus to the view the plugin was | |
# launched from. When run from a widget, i.e. the console or | |
# an input panel, the active text buffer gets automatically | |
# focused, this code restores the focus to the correct view. | |
if self.view.window().active_view() != self.view: | |
self.view.window().focus_view(self.view) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment