Simplify your life with Sublime Text 2

image
Having recently discovered Sublime Text 2, I wondered how one could come up with such an effective tool. Anyone who is spoiled by all sorts of IDEs, usually does not see any advantages, except for the beautiful highlighting of the code (although that was what attracted me initially).

I will not discuss how good this editor is and paint all its functionality - there are already a lot of topics on this topic I just want to show how you can make the tool out of it, for which the $ 59 that the developers want for it is not a pity.


Plugins


The main advantage of Sublime Text 2 over the rest of the editors and IDE zoo for me is the ability to write your own plugins. You don’t need to study tons of literature, for example, if you are going to write a plug-in for Eclipse or Geany. Plugins are written in Python and the project website has an API Reference, which was enough for me to start expanding the editor to fit my needs.

I also really liked the fact that the editor has a handy python console that you can use to debug your plugin.

Wishlist


I will try to formulate some of the possibilities that I really needed and I did not find any ready-made solutions.

Creating backups

I am developing in php and working with many projects at the same time. It just so happened that the version control system for us for many reasons did not take root, so I needed a convenient mechanism for creating backups.

Prior to Sublime, I used Geany and it had a fairly convenient add-in, allowing you to make backup copies of editable files in the selected directory. The only drawback of this add-in was that backups were created every time it was saved. I wanted something similar in Sublime, but for backups to be created on demand (for example, by pressing hot keys). In fairness, I note that among the ready-made solutions there is a similar plug-in AutomaticBackups, but we will write our bike. For fun.

File comparison

When you are working on a project more than one, it may be necessary to look at the changes that another programmer has made (or compare the file with ftp with a local copy). Usually in this case I opened meld , selected the necessary files there and looked at the difference. I would like to entrust this function to the editor. I imagined it this way: I open two files in the editor, arrange the tabs one after another, press the hot key and meld opens with the selected file and the file whose tab in the editor goes next. It seemed to me very convenient.

List all open files

By the specifics of the work, I often need to make a list of edited files (yes, here it is life without svn). I would like to receive a list of open files also by pressing a hot key.

Let's get to work


I described the small minimum of features that I needed for a comfortable transition to Sublime.

Next I will show how plugins were written for my needs and how easy it was. I note that I wrote everything under Ubuntu and I can not even imagine how it is done on Windows.

Backup Plugin

Let's get started. Open the editor, select Tools-> New Plugin from the menu ... A tab with a blank of the form opens:

import sublime, sublime_plugin

class ExampleCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		self.view.insert(edit, 0, "Hello, World!")
	


We need to correctly name the class of our plugin and save it with the correct name. The class name must begin with a capital letter, if the name consists of several words, then the words should not have a separator and each new word should begin with a capital letter. The class name must end with the word Command. The file name should be written in small letters, and if the plugin name consists of several words, then the words should have a separator _
I got this script and saved it in the proposed directory ~ / .config / sublime-text-2 / Packages / User / with the name backup.py:

import sublime, sublime_plugin

class BackupCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		self.view.insert(edit, 0, "Hello, World!")
	


Let us describe the operation algorithm of our future plugin:
  • Get the name of the file that is open in the current tab;
  • Create a directory with the current date in the backup storage directory (we will separate backups by day);
  • Create in the directory with the current date a directory tree to our file (I will show it as an example);
  • Copy the file to this directory with the current time prefix;
  • Notify the user that a backup has been created.

The first thing we need to do is get the name of the open file. We look at the documentation and delve into the sublime object. The sublime object has an active_window function - which returns the window object that we are currently active. Each window has an array of views - these are our tabs. We need an active tab, so we can get the active view object by running the command:

	sublime.active_window().active_view()


View has the function file_name () that we need, which will return the path to the open file. In order to start debugging our plugin, you need to bind to some hot key. To do this, open the menu Preferences-> Key Bindings - User and write the backup command bind there for example to the F7 key:

[
	 { "keys": ["f7"], "command": "backup" }
]


Now we bring our plugin to the form:

import sublime, sublime_plugin

class BuckupCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		print sublime.active_window().active_view().file_name()


Save the file, open the Python console (ctrl + `) and press F7. The full path to the file is displayed in the console.

The only thing we need to learn from the API is the output of messages to the status bar. We look in the documentation and see the function

sublime.status_message('')


Further actions no longer require proceedings with the API and I am posting the full plugin code:

# -*- coding: utf-8 -*-
import sublime, sublime_plugin
import os, time, shutil, sys
# Без этой строчки будем иметь проблему с выводом кириллицы и ловить UnicodeDecodeError
sys.setdefaultencoding('utf-8')
# Пока не заморачивался с файлами настройки, поэтому путь до папки бэкапа указывается здесь
backup_dir = "/home/deadlink/backup/"
class BackupCommand(sublime_plugin.TextCommand):
 
    def run(self, edit):        
        self.create(sublime.active_window().active_view().file_name())

    def create(self, filename):
    	# Получаем текущее время
        now = time.localtime()
        # Формируем префикс вида ЧАС_МИНУТА_СЕКУНДА_
        prefix = str(now.tm_hour)+"_"+str(now.tm_min)+"_"+str(now.tm_sec)+"_"
        # Имя папки для сегодняшних бэкапов вида ГОД_МЕСЯЦ_ДЕНЬ
        stamp = str(now.tm_year)+"_"+str(now.tm_mon)+"_"+str(now .tm_mday)
        # Формируем путь вида /путь до папки бэкапов/текущая дата/полынй путь до папки с файлом/
        full_path = backup_dir+stamp+os.path.dirname(filename)
        # Проверяем, может такая директория уже есть
        if not os.path.isdir(full_path):
            os.makedirs(full_path) # если нет, создаем дерево директорий
        # Коипруем туда файл с префиксом
        shutil.copyfile(filename, full_path+"/"+prefix+os.path.basename(filename))
        # Сообщаем себе о том что все прошло успешно
        sublime.status_message("Резервная копия файла " + filename + " успешно создана!")


Now create a directory, for example ~ / backup /, write the full path in the plugin, then open the SideBar (menu View-> SideBar-> show). Now select File-> OpenFolder and open our backup folder. Now we always have a backup folder on the left. Press F7 at any time and see how backups appear on the left. Here's what it looks like for me:

image


Diff plugin

We prepare a new plugin called Diff, similar to how we did it when writing the Backup plugin. The logic of the plugin should be like this:
  • We get the path to the file in the active tab;
  • Check if we have a tab to the right of the current one;
  • We get the file name from the second tab;
  • Run meld and pass the names of two files to it;

Let's figure out how to get the view object following the active one. We have an array of all the tabs of the active window, this:

sublime.active_window().views()


In the array, the tabs are ordered in the order we see them in the editor. Each view has its own unique id. Then, to get the tab following the active one, we need to find the number of the active view and take the next one by the number. I got the following code:

# -*- coding: utf-8 -*-
import sublime, sublime_plugin
import subprocess

class DiffCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		cur = num = 0
		# Получим массив всех view
		views = sublime.active_window().views()
		# Пройдемся по всем view в поисках активного, сравнивая по id
		for view in views:
			if view.id() == sublime.active_window().active_view().id():
				# Если id совпало, то запомним номер активного view
				cur = num
			num += 1
		# Проверим не является ли активный view последним
		if num-1 != cur:
			# Запустим программу meld с параметрами
			subprocess.Popen([
				'meld', 
				views[cur].file_name(), 
				views[cur+1].file_name()
			]) 


For this plugin to work, you need to have meld installed. If you use Ubuntu, then you can install it by running the command:

	sudo apt-get install meld


Next, we need to bind the diff action to some hotkey. To do this, open the menu Preferences-> Key Bindings - User and write there a command bind for example to the F2 key:

[
	 { "keys": ["f7"], "command": "backup" },
	 { "keys": ["f2"], "command": "diff" }
]


Open two files, go to the first open file and press F2. The meld program opens, in which two open files are compared. Excellent!

List of open files

I will not describe the creation of this plugin in stages. The principle of work, I think, is easy to understand from the code:

import sublime, sublime_plugin

class OpenedFilesCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		# Получаем массив всех view
		views = sublime.active_window().views()
		files = ""
		# Записываем имя файла из каждого view в переменную files
		for view in views:
			files += view.file_name()+"\n"
		# Создаем новый файл 
		sublime.active_window().new_file()
		# Пишем в новый файл список файлов
		sublime.active_window().active_view().insert(edit, 0, files)



Do not forget to bind it to pressing a hot key, for example ctrl + shift + f:

[
	 { "keys": ["f7"], "command": "backup" },
	 { "keys": ["f2"], "command": "diff" },
	 { "keys": ["ctrl+shift+f"], "command": "opened_files" }
]


Now we press the key combination that we have selected and in a new tab we see a list of open files.

Conclusion


In this article I wanted to show how flexible it is to customize and expand the Sublime Text 2 editor. I hope after reading you will also have an interest and a desire to do something different, because it is so simple.
I am not good at programming in Python, so I can be mistaken in terms such as an array or a list. Perhaps something could have been made simpler or otherwise.