Search code examples
sublimetext3code-snippetstransclusion

Is it possible to include the output of one snippet inside another?


Life is short, so I am creating a set of snippets. One of them is named Muletilla.sublime-snippet and looks like this

<snippet>
  <content><![CDATA[
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

${1:}
]]></content>
  <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
  <tabTrigger>muletilla</tabTrigger>
  <!-- Optional: Set a scope to limit where the snippet will trigger -->
  <scope>source.php</scope>
  <description>Create the mulelitlla!</description>
</snippet>

The output is

if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

Another one is named CreateCustomController.sublime-snippet and looks like this:

<snippet>
  <content><![CDATA[
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

class Custom${1:Module}Controller extends SugarController
{
  public function action_${2:name}()
  {
    ${3:// add logic here}
  }
}
]]></content>
  <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
  <tabTrigger>controller</tabTrigger>
  <!-- Optional: Set a scope to limit where the snippet will trigger -->
  <scope>source.php</scope>
  <description>create a custom controller</description>
</snippet>

As you can see I need to use the muletilla output again, and I will need to do the same in other snipptes. It is possible to include the output of one snippet inside another?

In this case use the output of muletilla in any other snippet I create.

Thanks in advance


Solution

  • This sort of thing isn't directly possible in the sense that I'm guessing you want it, wherein you could add something to the second snippet to tell it that it should pull in the contents of the first snippet and insert it inline along with everything else.

    One might think that a plugin could catch when a snippet is about to expand and rewrite it on the fly. However there's no real way for a plugin to capture when a snippet is about to inserted in the general case, since buffer completions, sublime-snippet and sublime-completions files all blend together in the list of items that can complete.

    One potential solution would be a plugin that sources it's snippet data from somewhere else and offers them as customized completions instead of directly using snippets. In that case the plugin itself would be responsible for providing the inserted content, which it could rewrite as needed.

    Another solution would be a package that allows you to specify your snippets in an intermediate format so that it could generate the final snippets for you, in which case it would lift the burden of having to manually update all of the snippets when the sub-snippet changes.

    That said, I'm not aware of any existing package that does either of those things, unfortunately.

    For the case outlined in your question, if you specify the end of the first snippet as ${0} instead of ${1} Sublime will expand it and drop the cursor at the {$0} point without offering any fields for expansion. That would allow you to trigger it and then immediately trigger the second one without pressing tab first.

    That's only saving a key press, however.


    Although it's not a general solution, here's another method for doing what you want here using core Sublime.

    First you need to create a tmPreferences file in your User package that looks something like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
      <key>scope</key>
      <string>source.php</string>
      <key>settings</key>
      <dict>
        <key>shellVariables</key>
        <array>
          <dict>
            <key>name</key>
            <string>muletilla</string>
            <key>value</key>
            <string><![CDATA[if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');]]>
            </string>
          </dict>
        </array>
      </dict>
      <key>uuid</key>
      <string>75888874-b1ed-460d-ab0e-9d1741d50188</string>
    </dict>
    </plist>
    

    The name of the file doesn't matter (only the extension is important), and you need to change the scope to the appropriate one.

    Inside of the shellVariables you define your custom variables and their contents. This example creates a variable named muletilla with the contents being the first line of your first snippet (the common text).

    Note that the contents of the CDATA is inserted verbatim; this means that any leading or trailing newlines will go in, and you can't use any variable substitutions in the value because they will be inserted as-is and not expanded further.

    With that in place, your first snippet becomes:

    <snippet>
      <content><![CDATA[
    ${muletilla}
    
    ${0}
    ]]></content>
      <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
      <tabTrigger>muletilla</tabTrigger>
      <!-- Optional: Set a scope to limit where the snippet will trigger -->
      <scope>source.php</scope>
      <description>Create the mulelitlla!</description>
    </snippet>
    

    The second snippet becomes:

    <snippet>
      <content><![CDATA[
    ${muletilla}
    
    class Custom${1:Module}Controller extends SugarController
    {
      public function action_${2:name}()
      {
        ${0:// add logic here}
      }
    }
    ]]></content>
      <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
      <tabTrigger>controller</tabTrigger>
      <!-- Optional: Set a scope to limit where the snippet will trigger -->
      <scope>source.php</scope>
      <description>create a custom controller</description>
    </snippet>
    

    This works for cases like this, but falls down in cases where you want the common text to contain a place holder of some sort. It's also potentially more of pain in that you have to remember where you defined the variable if you want to change it, instead of having a simple reference to another snippet.