Sublime Forum

Parse two selected lines in html tags

#1

I would like to create a Sublime Snippet that allow me to create a HTML tags when I select two line of text. Is it possible to do that ?

For example:

MIXTURE
CUT OFF

I wish to have this result:

<li class="list-group-item">MIXTURE <kbd>CUT OFF</kbd></li>

Thanks for any reply.

0 Likes

#2

Snippets have access to the selected text via the ${SELECTION} snippet variable, but they operate over all of the selections simultaneously.

If you had two selections and you invoked a snippet, it would expand in both selected regions at the same time, and ${SELECTION} would represent each individual selection.

So in that regard, it’s not possible to do this with a standard snippet. However, it’s possible to extend the insert_snippet command that you would normally use to insert a snippet to make it multi-selection aware in a way that will make it work the way you want.

An example of that is this plugin, which implements a insert_snippet_with_selections command that mimics (and uses) the built in insert_snippet command. If you’re not familiar with how to put a plugin in place, this video on how to use plugins outlines the steps.

import sublime
import sublime_plugin


class InsertSnippetWithSelectionsCommand(sublime_plugin.TextCommand):
    """
    Implement a version of "insert_snippet" that provides access to each
    individual selection as numbered selection variables such as "SELECTION_1".
    This allows a snippet expansion to include multiple selections in the
    replacement.

    Since the normal "insert_snippet" command would insert contents in all
    selections, this version "joins" all of the current selections into a
    single selection first.
    """
    def run(self, edit, **kwargs):
        sel = self.view.sel()
        for idx in range(len(sel)):
            kwargs["SELECTION_%d" % (idx + 1)] = self.view.substr(sel[idx])

        self.view.sel().add(sublime.Region(sel[0].begin(), sel[-1].end()))
        self.view.run_command("insert_snippet", kwargs)

This version of the insert_snippet command makes all of the selections available as distinct things, so in addition to ${SELECTION} being the entire selection, ${SELECTION_1} represents the first selection, ${SELECTION_2} represents the second, etc.

It also modifies the selection in the buffer so that instead of there being two selections (for example) there will be one selection that covers the entire selected area. That means that to use this the content you want to join together needs to be adjacent in the file such as it is in your example above; the expansion is going to cover everything from the start of the first selection to the end of the last selection, inclusive.

With the command in place, you can create a key binding on the command that will trigger it:

{ "keys": ["ctrl+shift+h"], "command": "insert_snippet_with_selections",
  "args": {
    "contents": "<li class=\"list-group-item\">${SELECTION_1} <kbd>${SELECTION_2}</kbd></li>"
  }
},

You can then select the two items and hit the key and you’re good to go.

As a side note, selections in the buffer are always used in a top-down order, so the second selection will always be the text CUT OFF (in this case) regardless of what order you make the selections in.

0 Likes

#3

@Coussini, I was trying to think of a solution that would not require a plugin. Using regex in a snippet works fine. See my snippet below.

<snippet>
	<description>Wrap Twice</description>
	<content><![CDATA[
${SELECTION/([^\n]+)(\n)([^\n]+)(\n)?/<li class="list-group-item">$1<kbd>$3<\/kbd><\/li>/g}
$0
]]></content>
	<scope>text.html.basic</scope>
</snippet>

@OdatNurd, I will go through your plugin 101 video series at some point, but for now, plugins are too much for me.

1 Like