Plugin to Sublime Text for the publication of articles on Habr
- Tutorial

For writing articles on Habr I use the text editor Sublime Text. Why it is a good editor on Habré many times already wrote (for example here ). However, when writing an article, there is a moment when it needs to be transferred for publication to the Habr, well, you know: Habr-> Add post-> Name, hubs, text (Ctrl + C / Ctrl + V), labels, preview. At this moment, it turns out that somehow the text with pictures was made up on the Habré ugly, edits begin. Edit in browser? Inconvenient and unsafe. Edit in Sublime and constantly copy-paste? Inconvenient and annoying.
Therefore, I made for myself a small plug-in for Sublime, which can interact with Chrome and Habr, transferring the written text by hotkey to Sublime to the editor on the page for creating a new topic with the automatic click of the Preview button. This allows you to write an article in Sublime and with one click see the result of its display on Habré.
Under the cut, we will learn how to write plugins for Sublime and we will figure out how to interact with Chrome from the Python code using its remote debugging protocol. The complete plugin code on GitHub is attached.
Preamble
Plugin for Sublime Text 3 and Google Chrome. With minimal edits, it can be adapted for Sublime Text 2 and with slightly larger editions for Firefox. It's just that I’m not very interested, but who wants to - well, you know how the Fork button on GitHub works .
Creating a New Sublime Text Plugin
There is nothing easier. Run Sublime Text, go to the menu - " Tools -> New Plugin ... " and get a new text file with the following contents:
import sublime, sublime_plugin
class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.insert(edit, 0, "Hello, World!")
Press Ctrl + S and see that the save file dialog offers us to save it to the User folder in the user profile. We do not agree, we go one level up (to the Packages folder), create the SublimeHabrPlugin folder here and save our file in this folder with the name SublimeHabrPlugin.py. The beauty of Sublime Text is that you don’t need to build, connect, or restart anything. When a file is saved, it will be automatically uploaded to the editor. You can verify this by opening the Sublime Text console (press Ctrl + ~), something should be written in it,
reloading plugin HabrPlugin.HabrPluginnow you can run the command in the same console
view.run_command('example')and make sure that the phrase “Hello, World!” is added to the text of the current file. The command name “example” is derived from the name of the ExampleCommand class by dropping the “Command” suffix and converting the remainder of CamelCase to underscore_case. Those. to add the habr command, our class must be called HabrCommand.
Chrome Preparation
How will we transfer our text from Sublime Text to Google Chrome, and not just “somewhere else”, namely, in the text editor field on the page of the new Habr topic, with the subsequent click of the “Preview” button? This is where the Chrome Remote debugging protocol comes in.. This is a protocol that allows you to connect to Chrome, get a list of open tabs and complete control over each of them (modifying content, subscribing to events, executing JavaScript, setting breakpoints) - i.e. in fact, all that the “developer tools” that open on F12 allow you to do. I’ll reveal an even more terrible secret - these tools themselves work precisely according to this protocol, which makes it possible to connect from one Chrome to debugging pages in another Chrome, on a remote machine or in a mobile version.
To allow external connections, we need to run Chrome with the command-line parameter
chrome.exe --remote-debugging-port = 9222
Personally, I made a shortcut "Article on Habr" on the desktop, running chrome like this:
"C: \ Program Files (x86) \ Google \ Chrome \ Application \ chrome.exe" --remote-debugging-port = 9222 habrahabr.ru/topic/add
As a result, in one click we get the open page of the new article and Chrome launched with connectivity for remote debugging.
We write the plugin itself
The idea of the plugin is already clear. We will take all the text of the current file, connect to Chrome, get a list of tabs, connect to a tab with an open Habr page using the WebSockets protocol and execute Javascript, which will insert the text into the desired field and click the "Preview" button. So, let's go step by step.
How to take all the text of the current file
text = self.view.substr(sublime.Region(0, self.view.size()))
How to connect to Chrome and get a list of tabs
We need to connect via HTTP to the local machine on port 9222, get JSON with metadata, parse it and get from it a link to the connection string to the Habr tab using the WebSockets protocol.
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)
If you run this code, something like
ws://localhost:9222/devtools/page/FD3D0027-0D2D-4DD0-AD1C-156FCA561F7Ethis will be displayed in the console. This is the URL for connecting to the tab debugging.
Connect to tab debugging via WebSocket
The WebSocket protocol here was chosen by Chrome because of its convenience in two-way communications - the server can send notifications to the client about events that occur immediately, without waiting for its next request. WebSocket support, unlike HTTP, is not part of the standard Python library, so we have to take a third-party library. I found this one , it copes with the tasks.
So, we connect to the desired tab and execute, for starters, in its context a simple 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, which inserts the text into the editor on Habré and presses the button "Preview"
Well, this is quite simple:
document.getElementById('text_textarea').innerHTML = 'text';
document.getElementsByName('preview')[0].click();
Well, now it's all together.
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()
In order not to puzzle over the escaping of any quotes, slashes, and other special characters in the text when transferring from python to Javascript, I just wrap everything in Base64 in Python code and deploy it back to Javascript (thankfully, Base64 support is included in the standard libraries of both languages) .
We hang up the habr command on the hotkey in Sublime Text
Open "Preferences -> Key Bindings - User". We add there:
{ "keys": ["alt+shift+h"], "command": "habr" }
Result
Launch Chrome, open Sublime Text, write “Test”, press alt + shift + h:

Project on GitHub