- PVSM.RU - https://www.pvsm.ru -

Плагин к Sublime Text для публикации статей на Хабр

Плагин к Sublime Text для публикации статей на Хабр

Для написания статей на Хабр я пользуюсь текстовым редактором Sublime Text. Почему это хороший редактор на Хабре уже много раз писали (например тут [1]). Есть, однако, при написании статьи момент, когда её нужно переносить для публикации на Хабр, ну, знаете: Хабр->Добавить пост->Название, хабы, текст (Ctrl+C/Ctrl+V), метки, предпосмотр. В этот момент оказывается, что как-то текст с картинками сверстался на Хабре некрасиво, начинаются правки. Править в браузере? Неудобно и небезопасно. Править в Sublime и постоянно копипастить? Неудобно и надоедает.

Поэтому я сделал для себя небольшой плагин к Sublime, который умеет взаимодействовать с Хромом и Хабром, перекидывая по хоткею в Sublime написанный текст в редактор на странице создания нового топика с автоматическим нажиманием кнопки «Предпросмотр». Это позволяет писать статью в Sublime и в одно нажатие видеть результат её отображения на Хабре.

Под катом мы научимся писать плагины к Sublime и разберёмся как из питоновского кода взаимодействовать с Хромом, используя его протокол удалённой отладки. Полный код плагина на GitHub [2] прилагается.

Преамбула

Плагин под Sublime Text 3 и Google Chrome. С минимальными правками он может быть адаптирован под Sublime Text 2 и с чуть большими — под Firefox. Просто мне это не очень интересно, а кому хочется — ну, вы в курсе, как работает кнопочка Fork [3] на GitHub.

Создание нового плагина для Sublime Text

Нет ничего проще. Запускаем Sublime Text, идём в меню — "Tools -> New Plugin…" и получаем новый текстовый файл со следующим содержанием:

import sublime, sublime_plugin

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

Нажимаем Ctrl+S и видим, что диалог сохранения файла предлагает нам сохранить его в папку User в профиле пользователя. Не соглашаемся, переходим на один уровень вверх (в папку Packages), создаем здесь папку SublimeHabrPlugin и сохраняем наш файл в эту папку с именем SublimeHabrPlugin.py. Прелесть Sublime Text в том, что ничего не надо билдить, подключать, перезапускать. В момент сохранения файла автоматически произойдет его подгрузка в редактор. Проверить это можно открыв консоль Sublime Text (нажмите Ctrl+~), в ней должно быть написано что-то типа

reloading plugin HabrPlugin.HabrPlugin

теперь можно выполнить в этой же консоли команду
view.run_command('example')

и убедиться, что в текст текущего файла добавилась фраза «Hello, World!». Название команды «example» получилось из имени класса ExampleCommand путем отбрасывания суффикса «Command» и преобразования оставшейся части из CamelCase в underscore_case. Т.е. для добавления команды «habr» наш класс должен называться HabrCommand.

Подготовка Хрома

Каким же образом мы будем перекидывать наш текст из Sublime Text в Google Chrome, да и ещё не просто «куда-нибудь», а именно в поле редактора текста на странице нового топика Хабра, с последующим нажатием кнопки «Предпросмотр»? Здесь нам на помощь приходит такая штука, как Chrome Remote debugging protocol [4]. Это протокол, который позволяет подключится к Хрому, получить список открытых вкладок и полный контроль над каждой из них (модификацию контента, подписку на события, выполнениe JavaScript, установка breakpoint'ов) — т.е. фактически всё то, что позволяют делать «инструменты разработчика», которые по F12 открываются. Я открою даже более страшный секрет — сами эти инструменты именно по этому протоколу и работают, что даёт возможность подключится из одного Хрома к отладке страниц в другом Хроме, на удалённой машине или в мобильной версии.

Для разрешения внешних подключений нам нужно запускать Хром с параметром командной строки

chrome.exe --remote-debugging-port=9222

Лично я сделал себе ярлычек «Статья на Хабр» на рабочем столе, запускающий хром вот так:

«C:Program Files (x86)GoogleChromeApplicationchrome.exe» --remote-debugging-port=9222 habrahabr.ru/topic/add/ [5]

В итоге в один клик получаем открытую страницу новой статьи и Хром запущенный с возможностью подключения для удалённной отладки.

Пишем сам плагин

Идея плагина уже понятна. Мы будем брать весь текст текущего файла, подключаться к Хрому, получать список вкладок, коннектится к вкладке с открытой страницей Хабра по протоколу WebSockets и выполнять Javascript, который вставит текст в нужное поле и нажмет кнопку «Предпросмотр». Итак, идём по шагам.

Как взять весь текст текущего файла

text = self.view.substr(sublime.Region(0, self.view.size()))
Как подключиться к Хрому и получить список вкладок

Нам нужно подключиться по HTTP к локальной машине на порт 9222, получить JSON с метаданными, распарсить его и получить из него ссылку на строку подключения к вкладке с Хабром по протоколу WebSockets.

import json
import urllib
import sys

def download_url_to_string(url):
	request = urllib.request.Request(url)
	response = urllib.request.urlopen(request)
	html = response.read()
	return html

info_about_tabs = download_url_to_string('http://localhost:9222/json')
		info_about_tabs = info_about_tabs.decode("utf-8")
		decoded_data = json.loads(info_about_tabs)
		first_tab_websocket_url = decoded_data[0]['webSocketDebuggerUrl']
		print(first_tab_websocket_url)

Если этот код запустить — в консоли будет выведено что-то типа
ws://localhost:9222/devtools/page/FD3D0027-0D2D-4DD0-AD1C-156FCA561F7E

Это и есть URL для подключения к отладке вкладки.

Подключение к отладке вкладки по WebSocket

Протокол WebSocket здесь выбран Хромом из-за его удобства в двухсторонних коммуникациях — сервер может посылать клиенту нотификации о происходящих событиях немедленно, не дожидаясь его следующего запроса. Поддержка WebSocket, в отличии от HTTP, не входит в стандартную библиотеку Python, поэтому нам придётся взять стороннюю библиотеку. Я нашел вот эту [6], она вполне справляется с поставленными задачами.

Итак, подключаемся к нужной вкладке и выполняем, для начала, в её контексте простой Javascript:

import json
import urllib
import SublimeHabrPlugin.websocket


def send_to_socket(socket, data):
	socket.send(data)

def send_script_to_socket(socket, script):
	send_to_socket(socket, '{"id": 1, "method": "Runtime.evaluate", "params": { "expression": "' + script + '", "returnByValue": false}}')

def on_open(ws):
	print('Websocket open')
	send_script_to_socket(ws, 'alert('Гав!')')

def on_message(ws, message):
	decoded_message = json.loads(message)
	print(decoded_message)
	ws.close()

def connect_to_websocket(url):
	res = SublimeHabrPlugin.websocket.WebSocketApp(url, on_message = on_message)
	res.on_open = on_open
	return res

first_tab_websocket = connect_to_websocket(first_tab_websocket_url)
first_tab_websocket.run_forever()

JavaScript, который вставляет текст в редактор на Хабре и нажимает кнопку «Предпросмотр»
Ну, это совсем просто:

document.getElementById('text_textarea').innerHTML = 'text';
document.getElementsByName('preview')[0].click();

Ну и теперь всё это вместе.

import sublime, sublime_plugin
import json
import urllib
import SublimeHabrPlugin.websocket
import sys
import base64

def download_url_to_string(url):
	request = urllib.request.Request(url)
	response = urllib.request.urlopen(request)
	html = response.read()
	return html

def send_to_socket(socket, data):
	socket.send(data)

def send_script_to_socket(socket, script):
	send_to_socket(socket, '{"id": 1, "method": "Runtime.evaluate", "params": { "expression": "' + script + '", "returnByValue": false}}')

def on_open(ws):
	text_to_send = str(base64.b64encode(bytes(ws.text, "utf-8")), encoding='UTF-8')
	send_script_to_socket(ws, 'document.getElementById('text_textarea').innerHTML = atob('' + text_to_send + '');document.getElementsByName('preview')[0].click();')

def on_message(ws, message):
	decoded_message = json.loads(message)
	ws.close()

def connect_to_websocket(url):
	res = SublimeHabrPlugin.websocket.WebSocketApp(url, on_message = on_message)
	res.on_open = on_open
	return res

class HabrCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		text = self.view.substr(sublime.Region(0, self.view.size()))
		info_about_tabs = download_url_to_string('http://localhost:9222/json')
		info_about_tabs = info_about_tabs.decode("utf-8")
		decoded_data = json.loads(info_about_tabs)
		first_tab_websocket_url = decoded_data[0]['webSocketDebuggerUrl']
		print(first_tab_websocket_url)
		first_tab_websocket = connect_to_websocket(first_tab_websocket_url)
		first_tab_websocket.text = text
		first_tab_websocket.run_forever()

Для того, чтобы не ломать голову в экранировании всяких кавычек, слешей и прочих спецсимволов в тексте при передаче из питона в Javascript я просто заворачиваю в питоновском коде всё в Base64 и разворачиваю обратно в Javascript (благо, поддержка Base64 входит в стандартные библиотеки обоих языков).

Вешаем выполнение команды «habr» на хоткей в Sublime Text

Открываем «Preferences -> Key Bindings — User». Дописываем туда:

{ "keys": ["alt+shift+h"], "command": "habr" }

Результат

Запускаем Хром, открываем Sublime Text, пишем «Test», нажимаем alt+shift+h:

Плагин к Sublime Text для публикации статей на Хабр

Проект на GitHub [2]

Автор: tangro

Источник [7]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/57839

Ссылки в тексте:

[1] тут: http://habrahabr.ru/post/147856/

[2] код плагина на GitHub: https://github.com/ezhikus/SublimeHabrPlugin

[3] Fork: https://github.com/ezhikus/SublimeHabrPlugin/fork

[4] Chrome Remote debugging protocol: https://developers.google.com/chrome-developer-tools/docs/debugger-protocol

[5] habrahabr.ru/topic/add/: http://habrahabr.ru/topic/add/

[6] вот эту: https://pypi.python.org/pypi/websocket-client-py3/0.12.0

[7] Источник: http://habrahabr.ru/post/217121/