Remember search query in Sublime Text 3 - sublimetext3

Is there any way to make Sublime Text 3 remember the previously entered search query in the "go to anything"?
I frequently use the "go to anything" to jump to line numbers. If Sublime Text could remember the last entered query I would not have to remember the line number and type it again.
I am already familiar with the bookmarks function and it is not really an alternative solution.

You can use a plugin that saves the content of the gotoAnything-panel when its modified, and then puts the content on the panel when it gets opened.
Basic plugin example:
import sublime, sublime_plugin
class GotoAnythingSaver(sublime_plugin.EventListener): # Use EventListener
# In my case gotoAnything view id is 2.
def on_modified(self, view): # This is called when a view is modified (text changed)
if (view.id() == 2): # Save content
self.content = self.get_view_content(view)
def on_activated_async(self, view): # This is called when a view is activated
if view.id() == 2 and hasattr(self, 'content'): # Restore content if empty
if not self.get_view_content(view):
view.run_command('insert', {"characters":self.content})
def get_view_content(self, view):
return view.substr(sublime.Region(0, view.size()))
To save the plugin use menu Tools>new Plugin and then save it in the given folder (folder name should be User), use fileName GotoAnythingSaver.py.
Example result used to go to the same line again:
Edit: tested on Sublime Text 3 build 3103 on Linux Mint and Windows 10. OP says this plugin leave the gotoanything dysfunctional, this doesn't happened to me, but be careful.
I would appreciate if someone could test it or help me, because I am not sure if the identifier of the view associated with goto-anything panel is always 2.

Related

Automatically loading a Sublime workspace on startup

I posted a question here: Easier access to Sublime macros
That was brilliant and worked perfectly! But there is one last thing I can't figure out. How do I set it so Sublime loads a saved workspace automatically whenever it is opened? Because I have my macros folder in the sidebar but I want it there automatically every time I start Sublime, I added the folder and saved the workspace but when I close Sublime the folder is gone and I have to go to project->open-recent->myworkspace
Is there a way to either load a workspace automatically upon starting Sublime or automatically adding a folder to the project when starting Sublime?
If the appropriate folder is open in the side bar and you have the hot_exit setting turned on (which it is by default), then quitting Sublime and restarting it should keep the folder there, and the same should be true for individual windows if they are referencing a project or workspace.
It's not uncommon to run afoul of the distinction that closing the last window is not the same thing as quitting Sublime depending on the platform that you're on, which can cause Sublime to record in the session that there were no windows open when it quit.
However based on the comments on your question it sounds more like this isn't actually your issue and you'd instead like a way to have a specific folder or folders added to newly created windows and projects instead, so that your macros folder is just always available.
This is possible via a plugin, which can adjust the folders in a particular window as appropriate. This can be semi-mostly automated (more on that below). Here's the plugin we need for this:
import sublime
import sublime_plugin
class AddAdditionalFoldersCommand(sublime_plugin.WindowCommand):
"""
Adds all of the folders from the `additional_folders` setting in the user
preferences to the current window, if they are not already present there.
"""
def run(self):
# Get the list of folders from the settings
settings = sublime.load_settings("Preferences.sublime-settings")
additional = settings.get("additional_folders", [])
# Get the project data and associated folders from the current window,
# with sensible defaults if the window has no project data yet
project_data = self.window.project_data() or {}
project_folders = project_data.get("folders", [])
# Add in any additional folders that are not already present
for folder in additional:
if not any(o["path"] == folder for o in project_folders):
project_folders.append({"path": folder})
# Update project data
project_data["folders"] = project_folders
self.window.set_project_data(project_data)
class AdditionalFolderListener(sublime_plugin.EventListener):
"""
Listens to see if a new window has been created, and if so add additional
folders if desired.
"""
def on_post_window_command(self, window, command_name, args):
if command_name == "new_window":
settings = sublime.load_settings("Preferences.sublime-settings")
if settings.get("auto_add_additional_folders", False):
window = sublime.active_window()
window.run_command("add_additional_folders")
To use this, you also need to add some settings to your Preferences.sublime-settings file (change the values as appropriate):
// When true, the folders in "additional_folders" will be
// automatically added when a new window is created.
"auto_add_additional_folders": true,
// Additional folders to add; these must be absolute paths
"additional_folders": [
"/home/odatnurd/.config/sublime-text-3/Packages/User/macros"
],
This does two things:
When you run the add_additional_folders command, all of the folders from the list configured in the additional_folders setting will be added to the current window so long as they are not already present (otherwise the same folder would appear more than once).
When a new window is created, if the auto_add_additional_folders setting is true (it defaults to false in the code for safety), then the command will be executed automatically for the new window.
Cycling back to the above post, the "semi-mostly automated" part of this is because in order to work, the new_window command has to be invoked for the event handler to be able to know that a new window was created.
This will be the case if you use the menu item or key binding to create a new window, but that command isn't executed if you open an existing project or workspace. It's also possible for windows to be created without using this command, such as if you quit Sublime with no windows and it creates a new one by default, or on MacOS if there are no windows and you click the dock icon.
Thus depending on how you use Sublime you may want to bind add_additional_folders to a key so that you can invoke it manually as needed. You can also create a file named something like AdditionalFolders.sublime-commands in your User package with the following content to add the command to the command palette. You can adjust the caption to suit; also the name of the file is not important so long as the extension is correct.
[
{ "caption": "Add Additional Folders", "command": "add_additional_folders" },
]
Final Notes:
Despite the name of the API used here, this will work in all windows even if they don't have a project associated with them (or they just have a workspace associated with them) because all windows inherently carry project data
It's been hinted that the next version of Sublime will have additional events that make it easier to detect new windows. The above covers up to build 3211; if you're reading this and there is a newer build available, the above could possibly be made smarter.

How to change the Sublime Text 3 StatusBar message in a command or macro (no plugin)?

addressing Sublime Text 3 users here.
I wrote a couple of macros to enable spell-check and load a specific dictionary, as I constantly swap between French and English and I wanted a simple shortcut for this (instead of browsing the menu or two successive commands in the command pallet).
My macros work as expected (french-spellcheck.sublime-macro, english-spellcheck.sublime-macro).
But I would like to display a message in the Status Bar, for instance "Switched to French" or "Switched to English" (for some time, let say 5 sec).
I looked everywhere I know and I tried for some time, but apparently there is no way to do this in a command (that could be added at the end of the macro), as the set_status internal ST3's Python API command (from Window package) is only available for plugins...
Does any one has an idea of how to display a message to the SublimeText3 StatusBar in a command/macro and not with a plugin? Thanks!
There is no built in command that invokes the API methods for doing this (at least not a documented one), so there's no way to go about this without a plugin of some sort.
That said, in order to do what you want, the following is all you would need to save into a file named e.g. set_status.py in your Packages/User folder (alongside your macros). This provides a set_status command that takes a value named value for the text to display, as mentioned in the commented out portion of your macro file.
import sublime, sublime_plugin
class SetStatusCommand(sublime_plugin.TextCommand):
def run(self, edit, value="set_status: use arg 'value' to set text"):
self.view.window ().status_message (value)
This uses a different API than the one you mention in your macro file comments; status_message does the work of displaying a message in the status bar, waiting a few seconds, and then removing it, which makes the command simple to implement.
If you wanted more control (i.e. to change the duration) you would need to modify this to invoke the API commands your macro files already mention: view.set_status() and sublime.set_timeout().

Sublime Text Keybinds Context Within Unsaved Changes

I am trying to create a keybind within sublime text 3 that changes its behavior depending if a file has unsaved changes or not.
Sublime already supports a context option within the creation of keybinds such as this:
{"keys":[":","e"],"command":"revert","context":[{"key": "setting.command_mode", "operand": true}]},
However I can't find if there is a way to detect if the file is saved or dirty.
Anyone have some insights on this?
Looking at the unofficial documentation (the official documentation is out-of-date), there seems to be no context that can be used in keybindings to determine if a file is saved or dirty.
I think it would therefore be necessary to create a plugin in Python, with a command which would perform the actions you require based on whether the file has unsaved changes or not. You could then set the keybinding to execute this command regardless of context, as the plugin will contain the necessary logic.
The official documentation mentions that a plugin can determine whether a file is saved or not using the is_dirty() method on the view.
I see from your question that you want to execute the revert command, so I have drawn up a quick and simple Sublime Text plugin / python script that will achieve this:
import sublime, sublime_plugin
class RevertIfUnsavedCommand(sublime_plugin.TextCommand):
def run(self, edit):
if self.view.is_dirty():
self.view.run_command('revert')
else:
print('TODO: do something else here')

Sublime Text: set language for each file

In Sublime Text (3), I can select the dictionary to use for spell checking. However, this setting seems to be global rather than on a per-file basis.
This is annoying when I'm working on multiple files that use different languages.
How can I achieve that Sublime Text remembers what dictionary to use for a file?
Usually it is needed to set settings based on the syntax of the file (one dictonary for javascript files, another for css files, etc.). You can achieve this goal easily using syntax specific settings. But there are also times where you need file-specific settings (files with the same syntax with different settings values). I give you example solutions for both cases.
File-specific way
In order to set view-specific settings (similar to file-specific) you can write a plugin. This simple example shows an input panel in which you can set the desired dictionary for the opened file.
import sublime, sublime_plugin
class Example(sublime_plugin.TextCommand):
def run(self, edit):
"""Default dictionary (caption)"""
defaultDict = 'Packages/Language - English/en_US.dic'
if self.view.settings().get('spell_check') == True and self.view.settings().get('dictionary') != None:
defaultDict = self.view.settings().get('dictionary')
"""Show panel to input dictionary name"""
self.view.window().show_input_panel('Dictionary value (cancel to disable spell check)', defaultDict, self.setDictionary, None, self.disableSpellCheck)
def setDictionary(self, dictionary):
"""Enables spell check and sets the dictionary (it is associated with the view)"""
self.view.settings().set('spell_check', True)
self.view.settings().set('dictionary', dictionary)
def disableSpellCheck(self):
self.view.settings().erase('spell_check')
self.view.settings().erase('dictionary')
Save it as example.py inside Packages>User. Then add a key-binding and trigger it when you're focused in the desired view:
{ "keys": ["ctrl+alt+e"], "command": "example" }
Note that this is view-specific, so if you close sublime and then re-open it the setting is restored, but if you close the file tab the setting is lost, so if you open the file in the future you'll have to set the setting again. To add a real file-specific setting you need a more complex plugin that extends EventListener and reads the files names in order to set the syntax.
Syntax-specific way
In addition to default settings and user settings you can use syntax speific settings.
Lets say you want to set the dictonary for javascript files, to add the required syntax specific settings open a javascript source file, then go to menu Preferences>Settings-more>Syntax-specific-user, and in the file opened set the settings:
{
"spell_check": true,
"dictionary": "Packages/Language - English/en_GB.dic"
}
Finally save it and now your javascript files are using the specified dictonary. Repeat the proccess for other file types.
Note that this is not file-specific but syntax-specific, so if you really need different dictionaries for different javascript files (for example) you'll need to use the other way.

Copy-Paste In Python

I am new to python. So I wanted to improve my skills. Before posting this question I tried to find some code or an idea that would guide me with what I intend to do. I did see some examples and posts on SO and other sites. But they all(the ones I came across) showed how to do it for single object. Below is what I want to do.
I want to write a utility in python that would allow me to choose from content I want to paste based on what my last 10,say, copy commands were for.
suppose I clicked copy when selecting a folder and then later I selected some text and pressed ctrl+c. Now I want to get option that would let me paste both the folder as well as the text.
Is that possible?
Thanks.
You could save the last 10 text items from a clipboard using tkinter:
#!/usr/bin/env python3
from tkinter import Tk
from collections import deque
def call_repeatedly(root, delay, func, *args):
func(*args)
root.after(delay, call_repeatedly, root, delay, func, *args)
def poll_clipboard(root, items):
text = root.clipboard_get()
if not items or items[-1] != text:
items.append(text)
def main():
root = Tk()
root.withdraw() # hide GUI
clipboard_items = deque(maxlen=10) # save last 10 clipboard items
call_repeatedly(root, 50, poll_clipboard, root, clipboard_items) # ms
call_repeatedly(root, 1000, print, clipboard_items) # print every second
root.after(10000, root.destroy) # exit in 10 seconds
root.mainloop()
main()
It polls clipboard every 50 ms. Polling is bad in general if there is an alternative interface that could allow you to subscribe to the clipboard events to be notified when new item is copied into the clipboard.
will it work for any kind of content, text, images etc. ?
This code works with text only. In general, you could get/set other types e.g., images (gtk, qt might provide a cross-platform way to do it).
Will it allow me to copy-paste text across all the applications ?
You are working with a clipboard so yes, it should work across all applications that can
work with a clipboard.
Can we make it work as normal ctrl+c (copy command)
Copy command can be implemented using a set command e.g., from pyperclip.py:
def gtkSetClipboard(text):
cb = gtk.Clipboard()
cb.set_text(text)
cb.store()
gtkSetClipboard(text) copies text to the clipboard.

Resources