When doing CTRL+SHIFT+P, there is command named Rot13 selection which allows to encrpyt the selected text.
I'd like to add a command named Rot47 selection that does:
selection = 'Test'
print ''.join(chr(33 + ((ord(ch) + 14) % 94)) for ch in selection)
#Output: %6DE
Where to write this Python code in SublimeText to have this new command present in CTRL+SHIFT+P?
You can write a plugin.
Some beginner (and not 100% correct) code is available here.
A complete plugin for Rot47 would have the following code:
import sublime, sublime_plugin
class Rot47Command(sublime_plugin.TextCommand):
def run(self, edit):
for region in self.view.sel():
if not region.empty():
s = self.view.substr(region)
s = ''.join(chr(33 + ((ord(ch) + 14) % 94)) for ch in s)
self.view.replace(edit, region, s)
Where to write this code?
Tools > New Plugin... will open a new buffer with some boilerplate code. Replace the boilerplate with the above code and save the file as rot47.py in /<sublime-text-dir>/Packages/User/.
You can test the above plugin by opening the console using Ctrl+`, and typing view.run_command('rot47') and hitting Enter. Make sure that you've selected some text before running your new Rot47 command.
Furthermore, if you want to create a keyboard shortcut for your new rot47 command: go to Preferences > Key Bindings -- User and add the following entry:
{ "keys": ["ctrl+shift+4"], "command": "rot47" }
(you can of course choose a more meaningful key combination.)
What did the above plugin code do?
self.view.sel() gives an iterable on the selected text regions (there can be multiple selections in the same buffer, go Sublime!). A region is basically a (start_index, end_index) pair that denotes a selected substring. self.view.substr(region) gives you the required substring.
We then modify the selection text (variable s) as desired and replace the selection with the new text (the call to self.view.replace()).
For a more extensive API reference, see this.
Related
When I pres ctrl+shift+F to search across all the files in the current scope, I get a new window listing all the files that contain that search term.
How can I quickly open all of these files?
Hold down the F4 key from the Search Results screen, and it will "Navigate to next match" - which causes it to open each file listed in the results.
Just a small note, if you are getting 10+ matches per file this method starts to fail because it gets slow.
Sublime doesn't have the ability to do this out of the box; however the plugin API gives you the power to create a plugin to do something like this fairly simply (depending on how you ultimately want it to work).
I assume there are plugins available for something like this, but for reference purposes here is a simple example:
import sublime
import sublime_plugin
class OpenAllFoundFilesCommand(sublime_plugin.TextCommand):
def run(self, edit, new_window=False):
# Collect all found filenames
positions = self.view.find_by_selector ("entity.name.filename.find-in-files")
if len(positions) > 0:
# Set up the window to open the files in
if new_window:
sublime.run_command ("new_window")
window = sublime.active_window ()
else:
window = self.view.window ()
# Open each file in the new window
for position in positions:
window.run_command ('open_file', {'file': self.view.substr (position)})
else:
self.view.window ().status_message ("No find results")
This provides a command named open_all_found_files which could be bound to a key, added to a menu, added to the command palette, etc.
Using the notion that sublime has a custom syntax for the find results with a scope dedicated to the matching filenames, this collects all such regions and then opens the associated files.
The optional command argument new_window can be passed and set to true to open the files in a new window; leaving it off or setting it to false opens the files in the same window as the find results. You can of course change the default as you see fit.
You can't do that from within Sublime Text.
If you are using Linux/UNIX/OSX you can open all the files that contain a particular string or matching regex by using a combination of grep and xargs on the command line using a command like this:
grep -rlZ "search_str_or_regex" /path/to/search/* | xargs -0 subl
// Command line options (may vary between OSes):
//
// grep -r Recurse directories
// grep -l Output only the filenames of the files which contain the search pattern
// grep -Z Output null terminated filenames
// xargs -0 Input filenames are null terminated
// xargs subl Sublime Text executable
//
// The combination of -Z and -0 allows filenames containing spaces to be handled
The files will be opened in the most recently used Sublime Text window. Add -n or --new-window after subl to have them opened in a new window.
If you are using Windows, consider using GOW or Cygwin.
I had to change the Code of OdatNurd. I had problems with the ":" and the " " at the end of the Filename... Under Mac OS Big Sur...
import sublime
import sublime_plugin
class OpenAllFoundFilesCommand(sublime_plugin.TextCommand):
"""
Collect the names of all files from a Find in Files result and open them
all at once, optionally in a new window.
"""
def run(self, edit, new_window=False):
# Collect all found filenames
positions = self.view.find_by_selector("entity.name.filename.find-in-files")
if len(positions) > 0:
# Set up the window to open the files in
if new_window:
sublime.run_command("new_window")
window = sublime.active_window()
else:
window = self.view.window()
# Open each file in the new window
for position in positions:
file = self.view.substr (position)
#print(file)
file = file.replace(":","")
window.run_command('open_file', {'file': file.strip()})
else:
self.view.window().status_message("No find results")
def is_enabled(self):
return self.view.match_selector(0, "text.find-in-files")
With Sublime Text, after a multi selection is with from CMD+D, I usually alter the text, then want to escape the selections and have the cursor at where the last selection was.
Some feature that are close:
CMD+U will reselect selections
CMD+G will skip to the next occurence
CMD+Shift+G will skip backwards to the previous occurence
Are there any keyboard shortcuts that will do what I want, similar to Esc but forward, rather than backward?
You can create a very simple plugin to do this, and then bind a key to it:
From the Tools menu, select Developer -> New Plugin...
Paste in the following:
import sublime, sublime_plugin
class SingleSelectionLastCommand(sublime_plugin.TextCommand):
def run(self, edit):
last = self.view.sel()[-1]
self.view.sel().clear()
self.view.sel().add(last)
self.view.show(last)
Save it in the folder it recommends, name the file something like single_selection_keep_last_cursor.py
Open Preferences -> Key Bindings - User
Type/Paste in something similar to the following:
{ "keys": ["escape"], "command": "single_selection_last", "context":
[
{ "key": "num_selections", "operator": "not_equal", "operand": 1 }
]
}
Save it
This example creates a new plugin with a command called single_selection_last, and then binds the Esc key to it when there is more than one selection. This therefore overrides the default Esc behavior of keeping the first selection.
The keybindings file needs to be a valid JSON array, so if it is empty you will need to wrap the example above in square brackets [...] for it to work.
If you wish to use a different key, you just need to replace escape with the key combination you want. See Preferences -> Key Bindings - Default for examples.
One more depth into my former question Why doesn’t this hotkey configuration for Sublime Text work?. Now I come to the implementation of sublime command, it is really a confusing way to hack what commands sublime has, as the exploration in former thread in order to find the command which is used to open a browser, finally I found it with help of #MattDMo.
Then I find there is one file named open_in_browser.py in the Packages/Default folder, I guess the commands is just the file name of .py files, but in fact I cannot find the corresponding file which could be named find_pre.py to command find_prev, then I copy open_in_browser.py as open_browsers.py, and add { "keys": ["ctrl+b"], "command": "open_browsers"} to sublime keymap, but it doesn't work. Then I realized that there should be some place which registers sublime commands to their implementation, so if there is such a mechanism, what is it? Where can I find it?
TL;DR
Create a file with any name in the Packages/User directory. Create a class in the file like MyTestCommand with a run method. Create a keymap using the class name in snake case and without Command suffix. Use named arguments to pass anything to the command.
Full answer
There is no need to register anything to create custom commands. Filename doesn't matter as Sublime Text simply scans it's directories for .py scripts and automatically executes them (registers them).
Here is the example script I use:
import sublime
import sublime_plugin
class ChangeViewCommand(sublime_plugin.WindowCommand):
def run(self, reverse=False):
window = self.window
group, view_index = window.get_view_index(window.active_view())
if view_index >= 0:
views = window.views_in_group(group)
if reverse:
if view_index == 0:
view_index = len(views)
if reverse:
new_index = view_index - 1
else:
new_index = (view_index + 1) % len(views)
window.focus_view(views[new_index])
So what it does - switches to the next/previous tab in the current group (the default behavior circles around all tab groups).
So we simply save it as any name in Packages/User directory.
Then we must create the key bindings in our user keymap file:
{ "keys": ["ctrl+tab"], "command": "change_view" },
{ "keys": ["ctrl+shift+tab"], "command": "change_view", "args": {"reverse": true} },
As you may see, the command is snake_case of the class name without the Command suffix. This will run the class's run method with named arguments.
Does this answer your question? For debugging in case of any errors - open the ST console (default shortcut is ctrl + `)
In Sublime Text, is there a way I can extract a selected piece of text into a separate file?
I do this often in LaTeX. Consider the following file:
main.tex
\section{Introduction}
...
...
\section{Conclusion}
I want to be able to select the text starting from Introduction until one line before the Conclusion, right-click and then say "Extract to file" (somewhat similar to how "Extract method" works in Visual Studio). Is there a way to achieve this using any shortcuts?
Bonus: Once the extraction is complete, substitute the extracted text with custom text such as \input{introduction} where introduction is the name of the file that the text was extracted into.
Nothing built in, but it's easily doable with a plugin. Note the following is minimally tested and won't handle everything in ST well. That being said, it should be a good base for you to start with. Just to be safe, I'd throw everything into a local git repo before using this to much. Hate for this to lead to loss of work. I copy the content being replaced to the clipboard just to be safe, but if you feel confident with it, you can remove sublime.set_clipboard(content)
import sublime
import sublime_plugin
import os
import re
class ExtractAndInput(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
self.region = view.sel()[0]
content = view.substr(self.region)
sublime.set_clipboard(content)
match = re.search(r"\\section{(.+?)}", content)
if match:
replace = "\\input{%s}" % match.group(1)
view.replace(edit, view.sel()[0], replace)
current = view.file_name()
new_file = "%s.tex" % match.group(1)
path = os.path.normpath(os.path.join(current, "..", new_file))
with open(path, "a") as file_obj:
file_obj.write("% Generated using ExtractAndInput Plugin\n")
file_obj.write(content)
After saving the plugin, you can create a key binding to extract_and_input. You can also add a context menu by creating a Context.sublime-menu in Packages/User with the following content.
[
{ "caption": "Extract to File", "command": "extract_and_input"}
]
What would be the best method, please, to insert file (foo.txt) into open file (bar.txt) at caret position?
It would be nice to have an open-file dialog to choose anything to be inserted.
The word processing equivalent would be "insert file" here.
Here is a substitute for foo.sublime-snippet, which can be linked to form files elsewhere:
import sublime, sublime_plugin
class InsertFileCommand(sublime_plugin.TextCommand):
def run(self, edit):
v = self.view
template = open('foo.txt').read()
print template
v.run_command("insert_snippet", {"contents": template})
From within a text command you can access the current view. You can get the cursor positions using self.view.sel(). I don't know how to do gui stuff in python, but you can do file selection using the quick panel (similar to FuzzyFileNav).
Here is my unofficial modification of https://github.com/mneuhaus/SublimeFileTemplates which permits me to insert-a-file-here using the quick panel. It works on an OSX operating system (running Mountain Lion).
The only disadvantage I see so far is the inability to translate a double-slash \\ in the form file correctly -- it gets inserted instead as just a single-slash \. In my LaTex form files, the double-slash \\ represents a line ending, or a new line if preceded by a ~. The workaround is to insert an extra slash at each occurrence in the actual form file (i.e., put three slashes, with the understanding that only two slashes will be inserted when running the plugin). The form files need to be LF endings and I'm using UTF-8 encoding -- CR endings are not translated properly. With a slight modification, it is also possible to have multiple form file directories and/or file types.
import sublime, sublime_plugin
import os
class InsertFileCommand(sublime_plugin.WindowCommand):
def run(self):
self.find_templates()
self.window.show_quick_panel(self.templates, self.template_selected)
def find_templates(self):
self.templates = []
self.template_paths = []
for root, dirnames, filenames in os.walk('/path_to_forms_directory'):
for filename in filenames:
if filename.endswith(".tex"): # extension of form files
self.template_paths.append(os.path.join(root, filename))
self.templates.append(os.path.basename(root) + ": " + os.path.splitext(filename)[0])
def template_selected(self, selected_index):
if selected_index != -1:
self.template_path = self.template_paths[selected_index]
print "\n" * 25
print "----------------------------------------------------------------------------------------\n"
print ("Inserting File: " + self.template_path + "\n")
print "----------------------------------------------------------------------------------------\n"
template = open(self.template_path).read()
print template
view = self.window.run_command("insert_snippet", {'contents': template})
sublime.status_message("Inserted File: %s" % self.template_path)