SublimeText plugin: making new view the active view - sublimetext2

I am writing a plugin for Sublime Text and am trying to run some view commands in a newly opened tab (those commands are in playback_text). However, the code below is executing the else statement. Am I missing something about focusing the view? Before calling run_command, I want the application to be focused on the new view.
class PlayInNewWindowCommand(sublime_plugin.WindowCommand):
def run(self):
newView = self.window.new_file()
sublime.active_window().focus_view(newView)
if sublime.active_window().active_view() == newView:
newView.run_command("playback_text")
else:
print sublime.windows()

I ran the code you provided with a very slight modification of my own (show a dialog box instead of running the "playback_text" command).
When I ran the command, it opened a new file and executed the if statement. Ignored the else statement. Not sure if you ran the command correctly. Also, you haven't included the import statement above your command so not sure if you have imported the required modules. Like this
import sublime, sublime_plugin
Here is the code I just ran:
import sublime, sublime_plugin
class PlayInNewWindowCommand(sublime_plugin.WindowCommand):
def run(self):
newView = self.window.new_file()
sublime.active_window().focus_view(newView)
if sublime.active_window().active_view() == newView:
sublime.message_dialog("new view is active view")
else:
print (sublime.windows())

Related

Sublime Text open all files containing search term

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")

Is it possible to show the size of files next to the file names inside Sublime sidebar?

I run an application that generates and updates a number of files in a specific folder. While the application runs, I observe the content of the folder through the sublime sidebar. Because I am interested to see the current size of each file while the application runs, I have an open terminal (Mac) where I use the following command to get the live state of the folder.
watch -d ls -al -h folderName
I was wondering if I can obtain this information directly from sublime.
So my question is: Is it possible to have the size of each file next to the file-names in the sublime sidebar? And if yes, how?
Since the sidebar is not in the official API, I don't think this is possible or at least it is not easy.
However getting the information into sublime text is easy. You can archive this by using a view. Just execute the ls command and write the result in the view.
I wrote a small (ST3) plugin for this purpose:
import subprocess
import sublime
import sublime_plugin
# change to whatever command you want to execute
commands = ["ls", "-a", "-s", "-1", "-h"]
# the update interval
TIMEOUT = 2000 # ms
def watch_folder(view, watch_command):
"""create a closure to watch a folder and update the view content"""
window = view.window()
def watch():
# stop if the view is not longer open
open_views = [v.id() for v in window.views()]
if view.id() not in open_views:
print("closed")
return
# execute the command and read the output
output = subprocess.check_output(watch_command).decode()
# replace the view content with the output
view.set_read_only(False)
view.run_command("select_all")
view.run_command("insert", {"characters": output})
view.set_read_only(True)
# call this function again after the interval
sublime.set_timeout(watch, TIMEOUT)
return watch
class WatchFolderCommand(sublime_plugin.WindowCommand):
def run(self):
folders = self.window.folders()
if not folders:
sublime.error_message("You don't have opened any folders")
return
folder = folders[0] # get the first folder
watch_command = commands + [folder]
# create a view and set the desired properties
view = self.window.new_file()
view.set_name("Watch files")
view.set_scratch(True)
view.set_read_only(True)
view.settings().set("auto_indent", False)
# create and call the watch closure
watch_folder(view, watch_command)()
Just open the User folder (or any other sub-folder of Packages), create a python file (e.g. watch_folder.py) and paste the source code.
You can bind it to a keybinding by pasting the following to your keymap:
{
"keys": ["ctrl+alt+shift+w"],
"command": "watch_folder",
},

ROT47 of the current selection in SublimeText

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.

Is there a way to extract the selected text into a new file in the same directory?

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"}
]

Insert file (foo.txt) into open file (bar.txt) at caret position

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)