Website development
Programming
August 2012 1

Упрощаем себе жизнь с помощью Sublime Text 2

From Sandbox
image
Недавно открыв для себя Sublime Text 2, я удивился тому, как можно было придумать настолько эффективный инструмент. Тот кто избалован всяческими IDE, обычно не видит никаких достоинств, кроме красивой подсветки кода (хотя именно это меня изначально и привлекло).

Я не стану рассуждать на сколько хорош этот редактор и расписывать весь его функционал — на хабре по этой теме уже есть множество топиков. Я лишь хочу показать как можно сделать из него тот инструмент, за который не жалко тех 59$, которые хотят за него разработчики.


Плагины


Главное преимущество Sublime Text 2 перед остальным зоопарком редакторов и IDE для меня — это возможность писать свои плагины. При этом не нужно изучать тонны литературы, как например если вы собрались писать плагин для Eclipse или Geany. Плагины пишутся на Python и на сайте проекта есть API Reference, которого мне было вполне достаточно для того чтобы начать расширять редактор под свои нужды.

Еще мне очень понравилось то, что в редакторе есть удобная консоль python, которую можно использовать для отладки своего плагина.

Хотелки


Попытаюсь сформулировать некоторые из возможностей, которые мне были очень нужны и готовых решений я не нашел.

Создание бэкапов

Я занимаюсь разработкой на php и работаю с множеством проектов одновременно. Так уж повелось, что система контроля версий у нас по множеству причин не прижилась, поэтому мне был жизненно необходим удобный механизм создания бэкапов.

До Sublime я использовал Geany и в нем была достаточно удобная надстройка, позволяющая делать резервные копии редактируемых файлов в выбранную директорию. Единственный минус данной надстройки был в том, что бэкапы создавались каждый раз при сохранении. Чего-то подобного мне и хотелось в Sublime, но чтобы бэкапы создавались по запросу (например при нажатии горячих клавиш). Справедливости ради отмечу, что среди готовых решений есть подобный плагин AutomaticBackups, но мы напишем свой велосипед. Ради забавы.

Сравнение файлов

Когда работаешь над проектом не один, то бывает необходимо посмотреть те измения, которые внес другой программист (или сравнить файл с ftp с локальной копией). Обычно в данном случае я открывал meld, выбирал там нужные файлики и смотрел разницу. Хотелось данную фунцию тоже возложить на редактор. Представлял я это так: открываю два файла в редакторе, располагаю вкладки одна за другой, нажимаю горячую клавишу и открывается meld с выбранным файлом и с файлом, вкладка которого в редакторе идет следующей. Мне показалось это очень удобным.

Список всех открытых файлов

По специфике работы, мне часто требуется составить список редактированных файлов (да, вот она жизнь без svn). Хотелось бы получать список открытых файлов тоже по нажатию горячей клавишы.

Приступим к работе


Я описал тот небольшой минимум фич, которые мне были необходимы для комфортного перехода на Sublime.

Далее я покажу как писались плагины для моих нужд и на сколько это было просто. Отмечу то, что писал я все под Ubuntu и как оно делается в Windows я даже не представляю.

Плагин Backup

Приступим. Открываем редактор, в меню выбираем Tools->New Plugin… Открывается вкладка с заготовочкой вида:

import sublime, sublime_plugin

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


Нам необходимо правильно назвать класс нашего плагина и сохранить его с правильным именем. Имя класса должно начинаться с большой буквы, если название состоит из нескольких слов, то слова не должны иметь разделителя и каждое новое слово должно начинаться с заглавной буквы. Заканчиваться имя класса должно обязательно на слово Command. Имя файла должно быть написано маленькими буквами и если название плагина состоит из нескольких слов, то слова должны иметь разделитель _

Я получил вот такой скрипт и сохранил его в предложенной директории ~/.config/sublime-text-2/Packages/User/ с именем backup.py:

import sublime, sublime_plugin

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


Опишем алгоритм работы нашего будущего плагина:
  • Получить имя файла, который открыт в текущей вкладке;
  • Создать в директории хранения бэкапов директорию с текущей датой (будем разделять бэкапы по дням);
  • Создать в директории с текущей датой дерево директорий до нашего файла (покажу на примере);
  • Скопировать файл в эту директории с префиксом текущего времени;
  • Известить пользователя о том, что бэкап был создан.

Первое что нам необходимо сделать — получить имя открытого файла. Смотрим документацию и вникаем в объект sublime. У объекта sublime есть функйия active_window — которое возвращает объект окна, которое у нас в данный момент активно. У каждого окна есть массив views — это наши вкладки. Нам нужна активная вкладка, поэтому мы можем получить объект активного view, выполнив комманду:

	sublime.active_window().active_view()


У view есть нужная нам функция file_name(), которая и вернет путь до открытого файла. Для того чтобы начать отлаживать наш плагин нужно сделать привязку к какой-нибудь горячей клавише. Для этого откроем в меню Preferences->Key Bindings — User и напишем там бинд комманды backup например к клавише F7:

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


Теперь приводим наш плагин к виду:

import sublime, sublime_plugin

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


Сохраняем файл, открываем консоль Python (ctrl+`) и жмем F7. В консоль выведется полный путь до файла.

Единственное что еще нам нужно узнать из API — это вывод сообщений в строку статуса. Заглядываем в документацию и видим функцию

sublime.status_message('')


Дальнейшие действия уже не требует разбирательств с API и я выкладываю полный код плагина:

# -*- 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 + " успешно создана!")


Теперь создаем директорию, например ~/backup/, прописываем полный путь в плагине, далее открывем SideBar (меню View->SideBar->show). Теперь выбираем File->OpenFolder и открываем нашу папку backup. Теперь у нас всегда слева открыта папка с бэкапами. Нажимаем в любой момент времени F7 и видим как слева появляются бэкапы. Вот как оно выглядит у меня:

image


Плагин Diff

Делаем заготовку нового плагина с именем Diff, аналогично тому, как мы это делали при написании плагина Backup. Логика работы плагина должна быть такая:
  • Получаем путь до файла в активной вкладке;
  • Проверяем есть ли у нас вкладка правее текущей;
  • Получаем имя файла из второй вкладки;
  • Запускаем meld и передаем ему имена двух файлов;

Давайте разберемся как получить объект view, следующий после активного. У нас есть массив всех вкладок активного окна, это:

sublime.active_window().views()


В массиве вкладки упорядочены в том порядке, как мы их видим в редакторе. У каждого view есть свой уникальный id. Тогда для получения вкладки, следующей за активной нам нужно найти номер активного view и взять следующий по номеру. У меня получился такой код:

# -*- 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()
			]) 


Для работы этого плагина нужно иметь установленную программу meld. Если Вы используете Ubuntu, то ее можно установить выполнив комманду:

	sudo apt-get install meld


Далее нам нужно привязать действие diff к какой-либо горячей клавише. Для этого откроем в меню Preferences->Key Bindings — User и напишем там бинд комманды например к клавише F2:

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


Открываем два файла, переходим в первый открытый файл и нажимаем F2. Открывается программа meld, в которой сравниваются два открытых файлика. Превосходно!

Список открытых файлов

Я не буду описывать поэтапно создание этого плагина. Принцип работы, думаю, легко понять из кода:

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)



Не забудьте только привязать его к нажатию горячей клавиши, например ctrl+shift+f:

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


Теперь нажимаем сочетание клавиш, которые мы выбрали и в новой вкладке видим список открытых файлов.

Заключение


В данной статье я хотел показать то, насколько гибко можно настроить и расширить редактор Sublime Text 2. Надеюсь после прочтения у Вас тоже появится интерес и желание сделать что-то свое, ведь это так просто.
Я не силен в программировании на Python, поэтому могу ошибаться в терминах, типа массив это или список. Возможно, что-то можно было сделать проще или иначе.

+52
50k 428
Comments 189
Top of the day