How can I apply black code formatting on save? - sublimetext3

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.

Related

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

Auto class importing in Sublime Text

I'm looking to get auto class importing In Sublime Text. I'm using HaxeFlixel and when ever I add a private var _btnPlay:FlxButton the needed import doesn't automatically show up. How can I get this to be the default action? Is there maybe a hot key I don't know about?
I know in FlashDevelop it can be done as the tutorial I'm following said that was the case:
We need to define a new FlxButton variable to use as our 'play' button. So, type:
private var _btnPlay:FlxButton;
Note: if you're using FlashDevelop, it should automatically create an import for FlxButton (import flixel.ui.FlxButton;) at the top of the class. This should be mostly automatic whenever you use class, but if it doesn't add it for some reason, you can enter it manually, or, highlight FlxButton in the editor and hit Ctrl+Shift+1 to add it.
It looks like "Organize Imports" adds missing imports - here's the pull request where this feature was added.
The default shortcut for it requires pressing Ctrl+Shift+H and Ctrl+Shift+O. You might want to reassign it to something shorter, maybe just Ctrl+Shift+O:
[
{ "keys": ["ctrl+shift+o"], "command": "haxe_organize_imports" }
]

Modify or extend default settings for a project

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.

How To Show Hello World with Glade/GtkD and the D Programming Language

On Ubuntu Linux, I can use the Glade application to create a Hello World dialog. Now how do I get the D programming language to display it?
Install the DMD compiler that compiles the D language on your Mac, Linux, or Windows computer. You can get more info about this here.
Install the Glade interface designer on your Mac, Linux, or Windows computer. You can get Glade on Ubuntu Linux quite easy with sudo apt-get install glade, but more information about installing on the various other platforms is here.
Install GTKd on your Mac, Linux, or Windows computer. This is not easy. You will need to start with the documentation at gtkd.org and then interact in the DLang.org Learn forum for more assistance if necessary.
Open Glade and create a new window that has a label on it saying Hello World.
Note that the tools palette in Glade shows a Window widget and an ApplicationWindow widget. Since we're not drawing any menus, ensure you're using the Window widget instead of the ApplicationWidget. If you fail to do that, you'll get warnings when running the application, talking about some missing menu calls.
Save it as hello.glade.
Open your hello.glade file in a text editor and look for a line that looks similar to this:
<object class="GtkWindow" id="window1">
Write down on a piece of paper that id attribute.
Create a hello.d script in the same directory as this hello.glade file and alter the following contents, changing the window1 to whatever was your id you wrote down earlier.
import gtk.Builder;
import gtk.Main;
import gtk.Widget;
import gtk.Window;
import std.stdio;
int main (string[] args)
{
Main.init(args);
Builder b = new Builder();
b.addFromFile("hello.glade");
Window w = cast(Window)b.getObject("window1");
w.addOnHide( delegate void(Widget aux){ Main.quit(); } );
w.showAll();
Main.run();
return 0;
}
Compiling is tricky. On Ubuntu Linux, I had to create a statement like the following. You may have to interact in the dlang.org Learn forums for your particular platform.
# dmd hello.d `pkg-config --cflags --libs gtkd3`
Once compiled, you can run your D executable to show the Hello World dialog. On Ubuntu Linux, I simply did:
# ./hello
Note that when you see the window and close it, you may receive some warnings on Linux. (On other platforms, this may vary.) I was receiving a warning about "Couldn't connect to accessibility bus - connection refused". The fix at least on Linux is to add this to your ~/.bashrc script at the bottom:
export NO_AT_BRIDGE=1
Now when you open the command prompt and run your compiled D command "hello" again,
it will not show that error.
If you get errors regarding menus, then you used an ApplicationWindow widget instead of a
Window widget, and will need to switch that in Glade.
Adding Buttons & Signals
The way I do it is to click on a widget in Glade, click Signals, find the event I want to add, such as clicked, and then in the Handler column, type in a function. For example, on a button1, I would type onButton1Clicked. Save the file.
Now, in your D source code, right after you create your Window object, add this code:
b.connectSignals(null);
...where b is your Builder variable.
In your D source code, add a function for this signal. For instance, I did:
extern(C) void onButton1Clicked()
{
writeln("got here");
Main.quit();
}
Note that in this case, the extern(C) is required.
Recompile and run your application. You'll see that it automatically runs your new function for that button click.

Gifs on Sailfish-os

I came here with a little problem, i can't use any local .gif in my code.
I work on Linux with QtCreator and the Sailfish VM to make a Sailfish-os application.
I tried first this example, without any success.
Rectangle {
width: animation.width
height: animation.height
AnimatedImage { id: animation; source: "../images/animatedimageitem.gif"}
}
The execution return :
QML AnimatedImage: Error Reading Animated Image File file:///bla/bla/.....
Same problem with other permissions on the gif and with an other gif.
After some researches I found this page where someone indicate to download a plugin, but Qt declare (I wish i could put a link but i'm new -_-', see comments) that gifs are already support by default.
The plugin was finally unobtainable and I found this Sailfish/bin/plugins/imageformats/libqgif.so in my directories.
So what can i do to show a gif on this damn thing ?
The error you are seeing is probably related to filepaths. Gifs are supported, AFAIK.
Instead of coding the path that way, consider the usage of a resource file to improve portability and platform independence.
Create a new resource file (File -> New File or project -> Qt -> Qt Resource File)
The property editor opens, click Add in the bottom then Add prefix and set a prefix such as / (or whatever you like!)
Click again to select Add files and insert your image
Right-click the newly added image entry and copy resource path to Clipboard
Build -> Run qmake (fundamental to ensure correct compilation)
The path you copied in the clipboard should be of the form:
://PATH_TO_IMAGE.gif
Now, given your QML code, I can guess the image folder is inside source code at the same level as the QML folder. Hence, if you added the .gif file from that folder you would have the following path in the clipboard:
://images/name.gif
This can be prepended with the prefix to obtain the final path. If your prefix is set to /, as we did above, the final string to be set in the source property of your AnimatedImage would be:
"qrc:///images/name.gif"
Obviously, a different prefix or a different path would result in a different final path.
Well..... I just put it on my phone (Jolla) and the gif works well. So this is the VM who doesn't seems to like gifs ...
Thanks for help though,
Psycho.

Resources