Sublime Forum

Context-sensitive snippet

#1

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:

function myfunc($a, $b)

I imagine I could write a plugin, that would expand it to

[code]/**

  • ${0}
  • @param ${1} $a
  • @param ${2} $b
  • @return ${3}
    */[/code]
    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 https://forum.sublimetext.com/t/tab-trigger-for-commands/2574/1&hilit=snippet#p14721, but it never had an answer.

0 Likes

#2

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

self.view.run_command("insert_snippet", {"contents": "Hello $1 and $0"})
0 Likes

#3

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. 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):

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 == '':
				continue
			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)

0 Likes

#4

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

[code]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][/code]

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.

0 Likes

#5

Hey, i actually just wrote a plugin which does this, and wrote about it on the Plugin Announcment forum about 5 minutes ago :smile: DocBlockr for Javascript, PHP and CoffeeScript

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

0 Likes

#6

I’ve seen it probably just as you’ve published it on github :smiley:

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.

0 Likes

#7

That could work, along with perhaps a configuration option? Fork the repo and send me a pull request :smile:

0 Likes