📜 ⬆️ ⬇️

Plugin for Sublime Text to publish articles on Habr



For writing articles on Habr, I use the text editor Sublime Text. Why is this a good editor on Habré already written many times (for example, here ). However, when writing an article, there is a moment when it needs to be transferred for publishing to Habr, well, you know: Habr-> Add post-> Name, hubs, text (Ctrl + C / Ctrl + V), tags, preview. At this point, it turns out that somehow the text with the pictures appeared ugly on Habré, the edits begin. Edit in the browser? Inconvenient and unsafe. Edit in Sublime and constantly copy-paste? Uncomfortable and annoying.

Therefore, I made for myself a small Sublime plugin that can interact with Chrome and Habr by transferring the text to the editor in the Sublime hotkey on the page for creating a new topic with automatic pressing of the Preview button. This allows you to write an article in Sublime and in one click to see the result of its display on Habré.
')
Under the cat, we learn how to write plugins for Sublime and figure out how to interact with Chrome from the Python code using its remote debugging protocol. The full code for the plugin on GitHub is attached.

Preamble


Plugin under Sublime Text 3 and Google Chrome. With minimal edits, it can be adapted for Sublime Text 2 and with slightly larger ones - for Firefox. It's just not very interesting to me, but whoever wants it - well, you know how the Fork button on GitHub works.

Creating a new plugin for Sublime Text


There is nothing easier. Launch Sublime Text, go to the menu - " Tools -> New Plugin ... " and get a new text file with the following content:

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 prompts us to save it to the User folder in the user profile. We do not agree, go up one level (in 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 the file is saved, it will automatically be loaded into the editor. You can check this by opening the Sublime Text console (press Ctrl + ~), something like this should be written in it

reloading plugin HabrPlugin.HabrPlugin

now you can run the command in the same console
view.run_command('example')

and make sure that the phrase “Hello, World!” was added to the text of the current file. The name of the command “example” was derived from the name of the class ExampleCommand by dropping the “Command” suffix and converting the remainder from CamelCase to underscore_case. Those. to add the “habr” command, our class must be called HabrCommand.

Preparing Chrome


How can we transfer our text from Sublime Text to Google Chrome, and even not just “somewhere”, namely in the text editor field on the page of the new Habr topic, followed by pressing the “Preview” button? This is where such a thing as the Chrome Remote debugging protocol comes to the rescue. This is a protocol that allows you to connect to Chrome, get a list of open tabs and full control over each of them (content modification, event subscription, JavaScript execution, installation of breakpoints) —that is in fact, all that “developer tools” allow to do, which are opened on F12, allow to do. I will reveal an even more terrible secret - these tools themselves work 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 a command line parameter.

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

Personally, I made myself a shortcut “Article on Habr” on the desktop, launching 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 an open page of the new article and Chrome launched with the ability to connect 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 the tab with the Habr page open using the WebSockets protocol and execute Javascript, which will insert the text in the required field and click the Preview button. So, go in steps.

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 a link from it to the connection string to the tab with Habr via 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 this will be displayed in the console.
ws://localhost:9222/devtools/page/FD3D0027-0D2D-4DD0-AD1C-156FCA561F7E

This is the URL for connecting to the debugging tab.

Connect to debug tabs by websocket


The WebSocket protocol here is selected by Chrome because of its convenience in two-way communications - the server can send notifications to the client about the events taking place immediately, without waiting for its next request. WebSocket support, unlike HTTP, is not included in the standard Python library, so we have to take a third-party library. I found this one , it copes well with the tasks.

So, we connect to the right tab and run, for the beginning, in its context, 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 text into the editor on Habré and clicks the Preview button
Well, it's completely 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 wrestle with shielding any quotes, slashes and other special characters in the text when transferring from python to Javascript, I simply wrap everything in Base64 in Python code and expand it back to Javascript (good, Base64 support is included in the standard libraries of both languages) .

We hang execution of the habr command on hot key in Sublime Text


Open “Preferences -> Key Bindings - User”. We add there:

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


Result



Run Chrome, open Sublime Text, write "Test", press alt + shift + h:



GitHub project

Source: https://habr.com/ru/post/217121/


All Articles