Ship default sublime-settings with package - sublimetext3

In my SublimeText package, I include a file BlameHighlight.sublime-settings. During testing, I link from ~/Library/Application\ Support/Sublime\ Text\ 3/Packages to this directory, and the changes to this file takes effect.
I also include a Menu item that points to ${packages}/User/BlameHighlight.sublime-settings. When I use the menu, I see a completely blank file.
How can I use my version of BlameHighlight.sublime-settings as the default template for ${packages}/User/BlameHighlight.sublime-settings?

Typically, your Main.sublime-menu will contain entries both for your default .sublime-settings file (probably Packages/BlameHighlight/BlameHighlight.sublime-settings) as well as the Packages/User version. If the user wants to customize the settings, they open the default file first (which is read-only in ST3) then the user one, and copy-paste what they need from default to user.
If for some reason you want the user file to be pre-populated with some settings, you'll need to programatically create it.

I would recommend to use the same strategy as every other package and not automatically create a copy of the default settings file. Not because I think its better, but because I think the user experience should not differ between the packages.
However as MattDMo stated you have to write our own plugin for this. At least for ST3 this is pretty straight forward:
import os, sublime_plugin, sublime
class CopyUserSettingsCommand(sublime_plugin.WindowCommand):
def run(self, package_name, settings_name):
file_path = os.path.join(sublime.packages_path(), "User", settings_name)
if not os.path.exists(file_path):
try:
content = sublime.load_resource("Packages/{0}/{1}".format(package_name, settings_name))
with open(file_path, "w") as f:
f.write(content)
except:
print("Error copying default settings.")
self.window.open_file(file_path)
Just copy this into a python file in your package and insert in the menu entry:
// ...
{
"command": "copy_user_settings",
"args": {
"package_name": "BlameHighlight",
"settings_name": "BlameHighlight.sublime-settings"
},
"caption": "Settings – User"
},
// ...

Related

How to use system default icons for a file type in a QTreeView

for reference this is all using Pyqt5 and Python 3.6:
I've got a QStandardItemModel that is built from QStandardItems that are strings of the items in a zip (the model displays all the contents of a zipfile). I went with this choice as I can not cache the files locally, and my research shows that QFileSystemModel can not work on archives unless I unpack at least temporarily.
All items in the QStandardItemModel end in the correct extension for the file (.csv,.txt,ect), and I need to display the icon a user would see if they were looking at the file in windows explorer, however show it in the qtreeview (a user seeing content.csv should also see the icon for excel). On that note, this application is only running on windows.
How can I pull the extensions default system file icon, and set it during my setting of these items? Would I have to manually download the icons for my known file types and do this, or does the system store it somewhere I can access?
Here's some basic code of how I build and display the model and treeview:
self.zip_model = QtGui.QStandardItemModel()
# My Computer directory explorer
self.tree_zip = QTreeView()
self.tree_zip.setModel(self.zip_model)
def build_zip_model(self,current_directory):
self.zip_model.clear()
with zipfile.ZipFile(current_directory) as zip_file:
for item in zip_file.namelist():
model_item = QtGui.QStandardItem(item)
self.zip_model.appendRow(model_item)
You can use QFileIconProvider:
def build_zip_model(self, current_directory):
iconProvider = QtWidgets.QFileIconProvider()
self.zip_model.clear()
with zipfile.ZipFile(current_directory) as zip_file:
for item in zip_file.namelist():
icon = iconProvider.icon(QtCore.QFileInfo(item))
model_item = QtGui.QStandardItem(icon, item)
self.zip_model.appendRow(model_item)

How to rename file tabs in Sublime Text 3

When working with an MVC framework, my controllers and models have similar names which can sometimes cause confusion when I have a controller and model files open and they are similarly named.
I looked through many settings and couldn't find any option to rename the tabs or group them together. There used to be a package for it however it was for Sublime Text 2.
I understand that I can simply rename the files themselves but I want them to be as closely named to the controller as possible.
Also in case anyone asks, I am using a self-made framework that we use for in-house internal systems.
Is there a way to accomplish this, or a method to better organise the file tabs so I can have all my models, controller, view and other files in some sort of tab-group? Or perhaps there is a view/layout that would address this issue?
I found the old RenameTab plugin for Sublime Text 2 and made a few little changes.
Create RenameTab.py and save it in the Packages/User folder:
import sublime
import sublime_plugin
class RenameTabCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.window().show_input_panel("Tab Name:", self.view.name(), self.on_done, None, None)
def on_done(self, input):
self.view.set_name(input)
Create Tab Context.sublime-menu and save it in the Packages/User folder:
[
{ "command": "rename_tab", "caption": "Rename Tab" }
]
Add this to the Sublime Keybindings settings (re-map it to whatever key you want):
{ "keys": ["alt+w"], "command": "rename_tab", "context":
[
{ "key": "setting.is_widget", "operator": "equal", "operand": false }
]
}
Right-click on the file tab at the top and press "Rename Tab" in the context menu and it will prompt you to create a new name. This won't change the filename, just the name of the tab for easy viewing/reading/organising.
RenameTab was written by frozenice, however he hasn't changed it since 2012, or tested it for Sublime Text 3. I changed the layout of the import sublime and import sublime_plugin. (I'm not even sure if that made a difference.)

Save file before running custom command in Sublime3

This question is similar to this one Is it possible to chain key binding commands in sublime text 2? Some years have passed since that question (and the answers given), and I'm using Sublime Text 3 (not 2), so I believe this new question is justified.
I've setup a custom keybind:
{
"keys": ["f5"],
"command": "project_venv_repl"
}
to run the project_venv_repl.py script:
import sublime_plugin
class ProjectVenvReplCommand(sublime_plugin.TextCommand):
"""
Starts a SublimeREPL, attempting to use project's specified
python interpreter.
Instructions to make this file work taken from:
https://stackoverflow.com/a/25002696/1391441
"""
def run(self, edit, open_file='$file'):
"""Called on project_venv_repl command"""
cmd_list = [self.get_project_interpreter(), '-i', '-u']
if open_file:
cmd_list.append(open_file)
self.repl_open(cmd_list=cmd_list)
def get_project_interpreter(self):
"""Return the project's specified python interpreter, if any"""
settings = self.view.settings()
return settings.get('python_interpreter', '/usr/bin/python')
def repl_open(self, cmd_list):
"""Open a SublimeREPL using provided commands"""
self.view.window().run_command(
'repl_open', {
'encoding': 'utf8',
'type': 'subprocess',
'cmd': cmd_list,
'cwd': '$file_path',
'syntax': 'Packages/Python/Python.tmLanguage'
}
)
This runs the opened file in a SublimeREPL when the f5 key is pressed.
What I need is a way to mimic the "Build" shortcut (Ctrl+B). This is: when the f5 key is pressed, the current (opened) file should be saved before running the project_venv_repl command.
Can this instruction be added to the project_venv_repl.py script, or to the command line in the keybind definition?
There's no need to do anything fancy. If you just want to save the current view before running the REPL, edit your ProjectVenvReplCommand class's run() method (which is called when the project_venv_repl command is executed) and add the following line at the beginning:
self.view.run_command("save")
This will silently save the current view unless it has not been saved before, in which case a Save As... dialog will open just like usual.
If you want to save all open files in the window, you can use this code:
for open_view in self.view.window().views():
open_view.run_command("save")

Automatically create folder of same name on file creation

Is there any way for me to set up sublime to automatically create a folder of the same name when I create certain files.
I create landing pages that all have a lp_ prefix to the filename, I would like to watch for when a file with this name is created and then automatically create a folder of the same name in another directory (for css and images).
Would this be possible with a plugin or something like Grunt?
Example:
Create file: lp_test.php
Automate Folder Creation: /lp/lp_test/
You can make a plugin that extends EventListener and overrides (for example) on_post_save_async. You can use this simple example as base:
import sublime, sublime_plugin, os
# We extend event listener
class ExampleCommand(sublime_plugin.EventListener):
# This method is called every time a file is saved (not only the first time is saved)
def on_post_save_async(self, view):
variables = view.window().extract_variables()
fileBaseName = variables['file_base_name'] # File name without extension
path = 'C:/desiredPath/css/' + fileBaseName
if fileBaseName.startswith('lp_') and not os.path.exists(path):
os.mkdir(path)
EDIT: changed on_post_save to on_post_save_async as it runs in a different thread and does not block the application. Thanks MattDMo for commenting it and for adding python highlighting.

Sublime Text 3: confirm to delete file

Is there a way to confirm deleting a file from the tree (left hand side) or remove the option from the context menu?
It is too easy to miss i.e. rename and click delete file instead. Then the file is gone.
I googled and found it should be moved to the trash folder but either that doesn't apply to Win7 or to using network drives. As a result the files are actually deleted or moved somewhere I have failed to track them down so far.
Using Sublime Text (build 3083)
Important: take a look at iron77 answer. It says that if you modify Default.sublime-package (options 1 and 3) this changes might be overriden if sublime text is updated.
Option 1: modify side_bar.py file
You can use sublime API to show an ok/cancel dialog. The code you are looking for is in a file called side_bar.py. This file is located inside the zip file Default.sublime-package. In windows this is usually located in C:\Program Files\Sublime Text 3\Packages\Default.sublime-package and can be explored using programs such as WinRar.
Inside that file locate DeleteFileCommand and add this 3 new lines, so it is changed from this:
class DeleteFileCommand(sublime_plugin.WindowCommand):
def run(self, files):
# Import send2trash on demand, to avoid initialising ctypes for as long as possible
import Default.send2trash as send2trash
To this
class DeleteFileCommand(sublime_plugin.WindowCommand):
def run(self, files):
isSure = sublime.ok_cancel_dialog('Are you sure you want to delete the file?')
if isSure != True:
return
# Import send2trash on demand, to avoid initialising ctypes for as long as possible
import Default.send2trash as send2trash
We are showing a ok/cancel dialog and if the user doesn't press Ok then we return and the file isn't removed.
Notes:
You will have to add the same code in class DeleteFolderCommand in order to confirm also when deleting folders.
Is a good practice to backup your Default.sublime-package file first just in case something goes wrong. EDIT: use a different folder for the backup or the package could be loaded twice causing problems as the OP has said in his comment.
As this is python code indentation is extremly important, don't
replace any spaces for tabs nor add any extra space or it will not
work (you can see it console).
Result:
Option 2: use an existing package
As user leesei said in his answer you can use SideBarEnhancements package to achieve your goal. This package adds many other features to the file context menu as you can see in the following image, but it is a very good choice as you only need to install an exsiting package.
Option 3: remove option from context menu
Edit Side Bar.sublime-menu inside Default.sublime-package (see option 1) and remove this line (and if you want remove also the line reffering to deleting folders):
{ "caption": "Delete File", "command": "delete_file", "args": {"files": []} },
While sergioFC's answers work great, I'm bit worried of modifying Default.sublime-package, as it might someday get overwritten when Sublime is updated, so the fix would need to be manually re-applied after each such update. SideBarEnhancements, on the other hand, might have too many features for someone who only wants the confirmation when deleting a file.
Alternatively, you can add a simple confirmation dialog that should be more resistant to ST updates, by creating a file (plugin). On Linux it should be somewhere around ~/.config/sublime-text-3/Packages/User/confirm_delete.py, and if you're on Windows/Mac or this path does not work for you, you can simply choose from the top menu: Tools -> Developer -> New Plugin and later save as confirm_delete.py - thanks to harrrrrrry for this suggestion. Code to put in:
from Default.side_bar import *
class DeleteFileCommand(sublime_plugin.WindowCommand):
def run(self, files):
if len(files) == 1:
message = "Delete File %s?" % files[0]
else:
message = "Delete %d Files?" % len(files)
if sublime.ok_cancel_dialog(message, "Delete") != True:
return
# Import send2trash on demand, to avoid initialising ctypes for as long as possible
import Default.send2trash as send2trash
for f in files:
v = self.window.find_open_file(f)
if v != None and not v.close():
return
send2trash.send2trash(f)
def is_visible(self, files):
return len(files) > 0
This code is basically a copy of DeleteFileCommand function from Default.sublime-package's side_bar.py combined with confirmation dialogs from DeleteFolderCommand from the same file, as Sublime has such dialog natively for folder removal.
When I choose delete by right clicking on a file in the SideBar, I get a confirmation.
Maybe it's SideBarEnhancements. It is worth a try.
WTF a software that doesn't have a confirm dialog before delete. I can't believe this. Sad but true. Just stupid software!
Unfortunately there is no way to activate a confirmation. Usually the the deleted file is moved to the trash folder but as you mentioned this is only true for local files. Files on a shared network drive are still deleted immediately. This is a Windows 'feature' :(
Locally the Recycle Bin is part of Windows Explorer -- and on the network you are NOT dealing with explorer on the server. Explorer locally isn't going to copy the file to the user's workstation just to put it into the recycle bin.
You CAN implement Microsofts Shadow Copy however, then users can undelete and compare versions. This would be the only way so far for network drives until the sublime developer decides to make an optional confirmation dialog.
According to #iron77's answer, the path for plugin could not exist (in my case). An easier way is:
1) Click Sublime Text topbar menu Tools -> Developer -> New Plugin.
2) Paste the snippet
from Default.side_bar import *
class DeleteFileCommand(sublime_plugin.WindowCommand):
def run(self, files):
if len(files) == 1:
message = "Delete File %s?" % files[0]
else:
message = "Delete %d Files?" % len(files)
if sublime.ok_cancel_dialog(message, "Delete") != True:
return
# Import send2trash on demand, to avoid initialising ctypes for as long as possible
import Default.send2trash as send2trash
for f in files:
v = self.window.find_open_file(f)
if v != None and not v.close():
return
send2trash.send2trash(f)
def is_visible(self, files):
return len(files) > 0
3) Save as confirm_delete.py.

Resources