Home Download Buy Blog Forum Support

Context-sensitive snippet

Context-sensitive snippet

Postby DSHugo on Fri Sep 23, 2011 11:34 am

I wonder if there is some way to register a snippet in ST2, that would run a specified plug-in instead of embedding a static text?
E.g. when commenting some piece of code, with intention to generate doxygen/phpdoc documentation later, I would like to analyze the declaration of a function I'm commenting on. For example the snippet is right before the piece of code:
Code: Select all
function myfunc($a, $b)

I imagine I could write a plugin, that would expand it to
Code: Select all
* ${0}
* @param ${1} $a
* @param ${2} $b
* @return ${3}

If the snippet is inserted right before the declaration of a class, the corresponding snippet would be generated.
I do realize it is possible to implement this function as a shortcut-invoked plugin, but:
  • it would look more streamlined when invoked as a snippet
  • inserting static text with a plugin would not respect tab-navigation through ${n} placeholders
Generally, the plugin would have to implement simple syntax analysis for a ton of languages. If there is a way to do what I just described, I would probably take up the work to provide snippets for as much languages as I could, since I think it would benefit a bunch of people.
There is already a related question http://www.sublimetext.com/forum/viewtopic.php?f=6&t=3110&p=14721&hilit=snippet#p14721, but it never had an answer.
Posts: 42
Joined: Tue Sep 20, 2011 11:05 am

Re: Context-sensitive snippet

Postby jps on Fri Sep 23, 2011 1:50 pm

It's not possible to do it in this manner, although you can have a plugin generate a snippet on the fly, e.g.,

Code: Select all
self.view.run_command("insert_snippet", {"contents": "Hello $1 and $0"})
Site Admin
Posts: 3217
Joined: Wed Mar 19, 2008 12:33 pm

Re: Context-sensitive snippet

Postby DSHugo on Mon Sep 26, 2011 8:52 am

I happened to find some code within the HTML/html_completions.py plugin, which did exactly what I asked for -- dynamic completion
However, I do have some questions, if someone happens to know an answer and minds to answer:
  • If the plugin accepts the EventListener parameter, and has an on_query_completions method, the method is called on "autocomplete" event. The method returns either [], or an array of unions. The question is -- what the first element of the union is for? E.g.
    Code: Select all
    return [(u'/**', snippet)]
    -- the first part doesn't seem to affect pretty much anything
  • What are the methods of identifying what is the syntax of current file? I want to return empty result as early as possible, if filetype is not yet supported (currently it is php only)

The code is as follows (please don't mock me yet):
Code: Select all
import sublime, sublime_plugin
import re

class CodeDoc(sublime_plugin.EventListener):
   def on_query_completions(self, view, prefix, locations):
      # only complete single line/selection
      if len(locations) != 1:
         return []
      # todo check is supported type of file (php)

      line = view.substr(sublime.Region(view.line(locations[0]).a, locations[0]))

      rex = re.compile("^\s*(.+)")
      m = rex.match(line)
      if not m:
         return []
      if m.group(1) != '/**':
         return []
      # find end of completion line
      currLineEnd = view.find('[\n\r]', locations[0])
      if currLineEnd is None:
         return []
      # find end of function/class declaration (php delimiter)
      nextLineEnd = view.find('[{]', currLineEnd.end())
      if nextLineEnd is None:
         return []
      declaration = view.substr(sublime.Region(currLineEnd.end(), nextLineEnd.begin()))
      if declaration.find("function") > -1:
         snippet = self.expandPhpFunction(declaration)
         if snippet:
            return [(u'/**', snippet)]
      elif declaration.find("class") > -1:
         snippet = self.expandPhpClass(declaration)
         if snippet:
            return [(u'/**', snippet)]
      return []
   def expandPhpClass(self, declaration):
      snippet = '/**\n'
      snippet += ' * ${1}\n'
      snippet += ' * @package ${2:default}\n'
      snippet += ' */'
      return snippet
   def expandPhpFunction(self, declaration):
      rex = re.compile("\((.*)\)", re.DOTALL)
      m = rex.search(declaration)
      if not m:
         return None
      params = m.group(1).split(',')

      snippet = '/**\n * ${1:Description}\n'

      i = 2
      for p in params:
         p2 = p.find('=')
         if p2 > -1:
            p = p[0 : p2]
         p = p.strip()
         p = p.replace('$', '\$')
         p = p.replace('&', '')
         if p == '':
         snippet += ' * @param ${' + str(i) + ':type} ' + p + ' ${' + str(i+1) + '}\n'
         i += 2

      snippet += ' * @return ${' + str(i) + ':type}\n'
      snippet += ' */'
      return snippet

Currently, I try to expand "/**" to a phpdoc block for a php class/function. If anybody has any suggestions on the code, I'd appreciate them a lot (since I have pretty much no knowledge in python, but I try to learn as I go)
Posts: 42
Joined: Tue Sep 20, 2011 11:05 am

Re: Context-sensitive snippet

Postby facelessuser on Mon Sep 26, 2011 5:02 pm

It is fairly easy to determine the current language and do something specific based on that language.

Code: Select all
from os.path import basename

#list supported syntax
syntax_list = {
  "PHP" : #syntax specific stuff here,
  "C++" : #syntax specific stuff here

#get current syntax
syntax = basename(self.view.settings().get('syntax')).replace('.tmLanguage','')

#is current syntax in your list
for item in syntax_list:
  if item == syntax:
    #Get syntax specifc stuff
    syntax_specific_stuff = syntax_list[item]

This code is not tested, but the basic idea is to keep a list with the different syntax as a key.
You get the current syntax and see if there is a key for it in the list, if it is in the list you can retrieve some syntax specific flag or or whatever: function call, flag, text.

You could do things completely different depending on what you need to do, but you can use the one line to retrieve syntax and do whatever you need to do with it.

Keep in mind that I am not really a python developer, nor do I understand the most pythonic ways to do things. I am just a guy who picks up languages as I need them.
Posts: 1780
Joined: Tue Apr 05, 2011 7:38 pm

Re: Context-sensitive snippet

Postby spadgos on Thu Oct 06, 2011 1:09 am

Hey, i actually just wrote a plugin which does this, and wrote about it on the Plugin Announcment forum about 5 minutes ago :) viewtopic.php?f=5&t=3307

Currently, it's geared to Javascript, but it shouldn't take too much effort to expand to give better support for PHP, C++, ... It'd be great to read your feedback
Posts: 121
Joined: Thu Oct 06, 2011 12:49 am

Re: Context-sensitive snippet

Postby DSHugo on Thu Oct 06, 2011 5:16 am

I've seen it probably just as you've published it on github :D

Looks and feels great, I'll try to merge the code I wrote for myself into it, since yours looks fancier. The only thing I don't like, though, is that "/**" triggers a documenting comment regardless of wether I want it or not — I think it should pop up in as autocompletion option.
Posts: 42
Joined: Tue Sep 20, 2011 11:05 am

Re: Context-sensitive snippet

Postby spadgos on Thu Oct 06, 2011 5:45 am

That could work, along with perhaps a configuration option? Fork the repo and send me a pull request :)
Posts: 121
Joined: Thu Oct 06, 2011 12:49 am

Return to Plugin Development

Who is online

Users browsing this forum: No registered users and 3 guests