Modify or extend default settings for a project - sublimetext3

In a sublimetext3 project, is there a way to modify or extend, not replace, default settings?
To be specific, in a project I specify the paths of the folders to include in the project. Each folder has files and directories unique to that folder that I want to exclude using either folder_exclude_patterns or file_exclude_patterns; see documentation for Projects.
But as I understand this, these project settings replace not extend the default settings. What I would like, however, is to have a project setting that appends to the default pattern rather than replacing it. Is this possible?
Pseudo code that expresses what I would like to do:
"folders":
[
{
"path": "c:\\dir1",
"folder_exclude_patterns": default_folder_exclude_patterns + ["junk"]
},
{
"path": "C:\\dir2"
"folder_exclude_patterns": default_folder_exclude_patterns + ["old"]
},
]
If this is not possible, then I believe the only thing I can easily do is copy the default settings and replicate them for each folder item. Since I have multiple projects/folders and need to do this for file exclude, folder exclude and binary file settings, this will get tedious and be hard to maintain. Of course, this seems like it is ripe for a plugin, but that is not in the scope of what I am looking to do. (Of course if someone else has a plugin that does something like this, I would be happy to try it out! :-))

Unfortunately, due to the way Sublime is set up, higher-precedence settings replace lower-precedence ones, not supplement them. This is a good thing because many settings are either/or - what would you do if your user settings had "highlight_line": false while a project had "highlight_line": true, for example?
A plugin should be able to do the trick. sublime.Window contains the project_data() and set_project_data() methods, which allow you to retrieve and write project settings, respectively. You could add a "more_folder_exclude_patterns" key to each folder in your project with the additional patterns you would like to add to the defaults set in your Preferences.sublime-settings file. The plugin could then check if the "more" key exists, read both arrays, concatenate them, and write the result back to the .sublime-project file, erasing the "more" key at the same time. Finally, you could set up an event listener to run the plugin whenever you wanted - on save, upon loading a new file, etc.
EDIT
Here's a working example:
import sublime
import sublime_plugin
from copy import deepcopy
class ModifyExcludedFoldersCommand(sublime_plugin.WindowCommand):
def run(self):
proj_data = self.window.project_data() # dict
orig_proj_data = deepcopy(proj_data) # for comparison later
settings = sublime.load_settings("Preferences.sublime-settings")
fep = settings.get("folder_exclude_patterns") # list
for folder in proj_data["folders"]:
try:
if folder["folder_exclude_patterns"]:
break # if f_e_p is already present, our work is done
except KeyError:
pass # if it doesn't exist, move on to mfep
try:
mfep = folder["more_folder_exclude_patterns"]
new_fep = sorted(list(set(fep + mfep))) # combine f_e_p from
# Preferences and project,
# excluding duplicates using
# a set.
folder["folder_exclude_patterns"] = new_fep
del folder["more_folder_exclude_patterns"]
except KeyError:
pass # if mfep doesn't exist, just move on to the next folder
if proj_data != orig_proj_data:
self.window.set_project_data(proj_data)
class UpdateProjectData(sublime_plugin.EventListener):
def on_activated(self, view):
window = view.window()
window.run_command("modify_excluded_folders")
Save the file as Packages/User/modify_excluded_folders.py (where Packages is the folder opened when selecting Preferences -> Browse Packages...) and it should go into effect immediately. It will run each time a view is activated. It checks for the presence of a "folder_exclude_patterns" array in each folder defined in the current .sublime-project file, and if found it assumes everything is OK and passes on to the next folder. If that array is not found, it then checks for the presence of a "more_folder_exclude_patterns" array. If found, it does its magic and merges the contents with the existing "folder_exclude_patterns" array from your preferences (Default or User). It then writes a new "folder_exclude_patterns" array into the folder and deletes the "more_folder_exclude_patterns" array. Finally, it checks to see if any changes were made, and if so it writes the new data back to the .sublime-project file.

Related

How can I apply black code formatting on save?

I would like to apply black whenever I save a Python file in Sublime Text 3. How can I do that?
(Bonus points if there is a quick way to disable it)
The answer above is really nice. In case you do not want to write your own package or plugin and you do not like the Formatter package, there is also the sublack package, which I think supports quick enabling/disabling of running black on save.
You install sublack the usual way via package control (Ctrl-Shift-P (Mac: Cmd-Shift-P) Package Controll: Install package). Afterwards you can format the current file either manually:
Run Black on current file:
Press Ctrl-Alt-B to format the entire file. You can also Ctrl-Shift-P (Mac: Cmd-Shift-P) and select Sublack: Format file.
or you can:
Toggle Black on save for current view :
Press Ctrl-Shift-P (Mac: Cmd-Shift-P) and select Sublack: Toggle black on save for current view.
Alternatively, you can also enable to run black on save permanently, by creating a user setting (Preferences -> Package Settings -> sublack -> settings) similar to the following:
{
"black_on_save": true,
"black_line_length": 80,
}
python 3.6
pip install black
cmd + shift + p then Click Package Control: Install Package
sublack
On terminal, which python
Preferences/Package Settings/sublack/Settings
On right panel
{
"black_on_save": true,
"black_command": WHICH_PYTHON_RESULT
}
To do something like this you would need a package or plugin that is capable of triggering an external command whenever an on_post_save event is triggered in Sublime for a Python file.
The Formatter package is an example of a package that does this sort of thing and it's README mentions that it supports Black as well. I don't use that package myself so I can't recommend it one way or the other. There may be other packages that provide a similar functionality as well, although this is the only one I spotted that mentions that it supports Black explicitly.
In theory any formatting package or package to execute commands on save events that lets you specify the command to execute could likely be configured to work as well.
For the sake of completeness, a plugin to do something like this can be created fairly quickly by creating a ViewEventListener that only triggers inside of Python files and uses the internal exec command to execute the black command.
An example of such a plugin would be the following (this video provides instructions on how to set up a plugin in Sublime if you're unsure how to do that) which for meta points has been formatted on save by itself:
import sublime
import sublime_plugin
import os
class FormatWithBlackOnSave(sublime_plugin.ViewEventListener):
"""
Listen for file saves and run the black code formatter on Python files
as they are saved, unless they have a setting indicating that the autoformat
should be disabled.
"""
#classmethod
def is_applicable(self, settings):
return "/Python/" in settings.get("syntax") and not settings.get(
"black_disabled", False
)
def on_post_save(self):
path, file = os.path.split(self.view.file_name())
settings = sublime.load_settings("Preferences.sublime-settings")
show_panel_on_build = (settings.get("show_panel_on_build", True),)
override_panel = settings.get("black_override_panel", False)
env = settings.get("black_env", {})
args = settings.get("black_arguments", [])
if override_panel:
settings.set("show_panel_on_build", not show_panel_on_build)
window = self.view.window() or sublime.active_window()
window.run_command(
"exec",
{
"shell_cmd": 'black {args} "{file}"'.format(
file=file, args=" ".join(args)
),
"working_dir": path,
"env": env,
},
)
if override_panel:
settings.set("show_panel_on_build", show_panel_on_build)
Once that's in place in your User package, you should also add some custom settings to your Preferences.sublime-settings file to control it:
// When this is True, the plugin will not execute on save.
"black_disabled": false,
// Override the value of the `show_panel_on_build` setting that controls
// whether the output panel appears when the command is executed. When
// true the value of that setting is temporarily inverted.
"black_override_panel": false,
// The arguments (other than the current file) to pass to black
"black_arguments": [],
// Optional environment variables to use while running the tool
// (for example to set the path); works as in a `sublime-build` file.
"black_env": {
}
This requires you to install the black command yourself (e.g. pip install black) and will execute it with the given arguments for the current file, so long as the file is a Python file and the black_disabled setting is set to false as above.
The plugin uses the internal exec command, which uses the preference show_panel_on_build to determine if a panel that shows you the output of the command should appear or not. The default value for that setting is true, which means that every time you save a Python file the panel would open with a message like:
reformatted black.py
All done!
1 file reformatted.
[Finished in 0.2s]
The black_override_panel setting makes the plugin invert the value of the show_panel_on_build while it's executing the command; either not showing it when it normally would or vice versa.
Altering the setting in your preferences will globally disable the event listener from triggering in all Python files. You can also create a file in your User package with a name like Black.sublime-commands with the following content:
[
{
"caption": "Black: Toggle Format-on-save for this view",
"command": "toggle_setting",
"args": {
"setting": "black_disabled"
}
}
]
That would add a command to the command palette that inverts the state of the setting in the current file; that would allow you to disable the plugin only for certain files or enable it only for certain files.

Sublime text 3 and overriding settings per project doesn't work

I've setup to my user settings on sublime text 3, the following option: trim_trailing_white_space_on_save: true
But it seems our collagues have a different opinion regarding of it and if it is useful. So I have to override this setting per project (I wanted to other projects, I just want to be skipped on a specific one). I've tried by creating a file .sublime-workplace and/or .sublime-project on the root of the project folder and I added the following:
{
"settings":
{
"trim_trailing_white_space_on_save": false
}
}
But when I save a file with whitespaces, it still keeps trimming them. What am I doing wrong? Is there specific settings which I can bypass per-project? If yes, is there another way of doing that (maybe a sublime plugin or something).
From the description in your question, the problem may be that you created the project file but didn't actually open it. Sublime only treats paths with a project file in them specially if you open the project, e.g. via Project > Open Project or from the command line via subl --project path/to/project.sublime-project.
Additionally, without a list of paths to include in the project, the project defaults itself to being empty, which is probably not what you want.
From your existing window that you're already working with, select Project > Save Project As... to save a project and workspace that captures the current state of everything; folders visible in the side bar as well as any files that may already be open. Once that's done you can use Project > Edit Project to edit the project file and add the setting as you did above.
Aside from that, a project file like the following saved into the folder you're using for your project will also do what you want; just remember to use Project > Open Project... or Project > Switch Project... to open the file you've created so that Sublime will use it.
{
"folders":
[
{
"path": "."
}
],
"settings":
{
"trim_trailing_white_space_on_save": true
}
}

How to change the project used by Indexer during a Vim session?

I'm trying to use Indexer (vimscript #3221) to index the files of a specific project created with project.vim (vimscript #69). As the plugin's documentation says, if I don't set the g:indexer_projectsSettngsFilename variable in ~/.vimrc, it uses ~/.vimprojects file by default. But I want to be able to specify the project without setting that variable in ~/.vimrc (i.e., I want to do it in a more local way).
I tried to set g:indexer_projectsSettngsFilename in _vimrc_local.vim, setting it manually after Vim is started, re-source the plugin (by running :so) once the variable has been set, run :IndexerRebuild afterwards. In either case the plugin does not create the index file for the project.
So how can I make Indexer to change its project during a Vim session?
The Indexer plugin has a dependency: Vimprj, which manages options for different projects: exactly what you need.
The Indexer repository has some examples under doc/examples directory. Check, for example, doc/examples/vimprj_indexer_files.
In short, in the root directory of your project, you need to create the .vimprj directory, and after this, when you open some file under your project directory, all .vimprj/*.vim files will be sourced, and $INDEXER_PROJECT_ROOT variable will be set to the path of your project (which is one level above the .vimprj dir).
I usually put my .indexer_files in .vimprj directory too, and refer to it from .vimprj/my.vim file like this:
" get path to ".vimprj" folder
let s:sPath = expand('<sfile>:p:h')
" specify our ".vimprj/.indexer_files"
let g:indexer_indexerListFilename = s:sPath.'/.indexer_files'
And I can refer from .indexer_files to $INDEXER_PROJECT_ROOT like this:
[my_project]
option:ctags_params = "--langmap=c:.c.h --languages=c"
$INDEXER_PROJECT_ROOT
For more information, see the article: Vim: convenient code navigation for your projects, which explains the usage of Indexer + Vimprj thoroughly.

Add a Timestamp to the End of Filenames with Grunt

During my Grunt tasks, add a unique string to the end of my filenames. I have tried grunt-contrib-copy and grunt-filerev. Neither have been able to do what I need them to...
Currently my LESS files are automatically compiled on 'save' in Sublime Text 3 (so this does not yet occur in my grunt tasks). Then, I open my terminal and run 'grunt', which concatenates (combines) my JS files. After this is done, then grunt should rename 'dist/css/main.css' and 'dist/js/main.js' with a "version" at the end of the filename.
I have tried:
grunt-contrib-copy ('clean:expired' deletes the concatenated JS before grunt-contrib-copy' can rename the file)
grunt-filerev ('This only worked on the CSS files for some reason, and it inserted the version number BEFORE the '.css'. Not sure why it didn't work on the JS files.')
Here's my Gruntfile.js
So, to be clear, I am not asking for "code review" I simply need to know how I can incorporate a "rename" process so that when the tasks are complete, I will have 'dist/css/main.css12345 & dist/js/main.js12345' with no 'dist/css/main.css' or 'dist/js/main.js' left in their respective directories.
Thanks in advance for any help!
UPDATE: After experimenting with this, I ended up using grunt-contrib-rename and it works great! I beleieve the same results can be achieved via grunt-contrib-copy, in fact I know it does the same thing. So either will work. As far as support for regex, not sure if both support it, so may be something else worth looking into before choosing one of these plugins :)
Your rename:dist looks like it should do what you want, you just need to move clean:dist to be the first task that runs (so it deletes things from the prior build rather than the current build). The order of tasks is defined by the array on this last line:
grunt.registerTask('default', ['jshint:dev', 'concat:dist', 'less:dist', 'csslint:dist', 'uglify:dist', 'cssmin:dist', 'clean:dist', 'rename:dist']);
That said, I'm not sure why you want this behavior. The more common thing to do is to insert a hash of the file into the filename before the file extension.
The difference between a hash and a timestamp is that the hash value will always be the same so long as the file contents don't change - so if you only change one file, the compiled output for just that file will be different and thus browsers only need to re-downloaded that one file while using cached versions of every other file.
The difference between putting this number before the file extension and after the extension is that a lot of tools (like your IDE) have behavior that changes based on the extension.
For this more standard goal, there are tons of ways to accomplish it but one of the more common is to combine grunt-filerev with grunt-usemin which will create properly named files and also update your HTML file(s) to reference these new file names
I'm not sure to understand completely what end you want, but if you add a var timestamp = new Date().getTime(); at the beginning of your gruntfile and concatenate to your dest param that should do the job.
dest: 'dist/js/main.min.js' + timestamp
Is it what your looking for?

How do I configure Textmate2 to turn off Soft Wrap by default for all files?

I put a .tm_properties file in my home folder and also at the top of my source tree with the following in it, none of the versions had any effect.
softWrap = false
softWrap = :false
softWrap=false
Also with no effect, I put the line below section headers such as:
[ source ]
[ text ]
[ "*.*" ]
Would love to know what I'm missing here.
I have the following entry below and it work just fine. Try removing other file type specific configuration to ensure its not being overridden.
softWrap = false
Also do check the "global" defaults under
TextMate.app/Contents/Resources/Default.tmProperties
If you haven't already seen https://gist.github.com/1478685, do check it out. The tm_properties in your most immediate folder should take priority. So try and remove other setting and just test it with this one.
Alternatively, remove all other tm_properties and just leave the "global" one and test it out.
Finally do check out this discussion and the version of TM2 you are using. There have been certain fixes to softwarp, and it could just be a bug that you are facing.
EDIT
Also just FYI, I found this item in the latest release notes of TM2.(2012-02-18)
Cached .tm_properties files are now observed via kevent so changes invalidate the cache immediately (previously it could take up to 30 seconds before the updated file was read). On file systems without kevent support you need to relaunch TextMate to flush the cache.

Resources