Sublime Forum

Snippet Triggering Inside Command

#1

I’m trying to create a command that moves the cursor to the beginning of the file and then triggers a particular snippet. There was another question long ago that said that calling clear before the snippet was the problem, but I believe I’ve fixed that by making sure that there’s a region set after that.

So, here’s my little plugin:

class vhdlModeInsertHeaderCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		print("Start of VMIH Command")
	    	bof = self.view.text_point(0,0)
	    	self.view.sel().clear
		self.view.sel().add(sublime.Region(bof))
		self.view.show(bof)
		print("Point moved to beginning of file.  Starting insert snippet command.")
		self.view.run_command("insert_snippet", {"name" : "Packages/vhdl-mode/Snippets/vhdl-header.sublime_snippet"})
		print("Post insert snippet command.")

So, I’ve tested the bits and pieces of this as follows:

  1. The point moving routine is basically just the default “goto_line.py” routine but customized for my desires. I COULD just call goto_line I suppose, but for training purposes I was typing it all out myself.

  2. In the console, I can type view.run_command(“insert_snippet”. …) and it inserts the snippet correctly at the location of the point. So I am pretty sure the actual command is right and it’s calling the snippet from the path, etc.

  3. I added the print statements just to see where it was ending up. It actually seems to get to the end. Here’s the output:

    view.run_command(“vhdl_mode_insert_header”)
    Start of VMIH Command
    Point moved to beginning of file. Starting insert snippet command.
    Post insert snippet command.

Any ideas? I guess the command is running alright, but it’s not displaying to the user and doesn’t insert the template text.

0 Likes

#2

I can see two problems with what you have here that are stopping it from doing what you want.

self.view.sel().clear

This call to the clear method on the selection is missing the () to make it a method call, which stops it from actually clearing the selection, so when the snippet insertion happens, it happens both at the start of the file and also at whatever cursor location(s) were already set.

self.view.run_command("insert_snippet", {"name" : "Packages/vhdl-mode/Snippets/vhdl-header.sublime_snippet"})

You have the extension here as sublime_snippet but the snippet extension is sublime-snippet, so when the command triggers it’s not finding the snippet file.

With those two things changed, it should do what you want (I tested it with the HTML/Snippets/html.sublime-snippet locally).

As an aside, the point position of the first character in the file is always 0, so if it’s always going to be jumping to that position to insert the snippet you don’t need to call text_point, but that’s not hurting anything and arguably makes the intention clearer.

1 Like

#3

Aha, I had completely missed the - to _ transposition. Since I’d typed it into the console (super useful tool there, along with the hint to turn on command logging – ran across that one at stack overflow) I must have typed it correctly, and then in the code mistyped it.

I’ll add the parens to clear. I will admit, here I was leaning on … nope, I just double checked and goto_line.py has () after clear. I just completely muffed it. So, if I’m reading this correctly, for self.view.sel().clear(), self is the self object passed to the method, view is the view class. Sel() is a method defined for view and also an object, and clear is a method defined for sel? Diving into the thick of OOP class hierarchies is dizzying sometimes.

Annnnd it works. Interesting, the final print command actually still prints. I suppose the editing process of typing into fields is spun off asynchronously with the plugin execution. That will make it trickier to do the next bit (I’d like to auto inject a date field, and some other fields from a settings file.) But baby steps!

0 Likes

#4

Yep, self refers to the specific instance of the command that’s responding to the command (as a TextCommand, there is a new instance of the class created for each view) and self.view is accessing a property named view that exists on all TextCommand objects which tracks the view that the command is operating inside of.

sel() is a method on the View class that returns an instance of a Selection object that represents all of the selections inside of that view, and that object has a clear() method to clear all of the selections away.

0 Likes

#5

If the date header and your other fields are going to be a part of the snippet that you’re expanding, you can do something like the following:

<snippet>
    <content><![CDATA[
Hello, ${1:this} is a ${2:snippet} I made ${DATE}.
]]></content>
</snippet>
snippet = "Packages/User/test.sublime-snippet"
view.run_command("insert_snippet", {"name": snippet, "DATE": "Today"})

Basically, the names of the extra arguments that you pass to the insert_snippet command can exist inside of the snippet and will be expanded based on their names matching.

As given above, the date won’t be editable though (i.e. it doesn’t count as a field stop in the snippet), so if you want to provide the current date but allow for hand editing, you have to expand the variable as the default for a field, such as:

<snippet>
    <content><![CDATA[
Hello, ${1:this} is a ${2:snippet} I made ${3:${DATE}}.
]]></content>
</snippet>
4 Likes

Easiest way to insert date/time with a single keypress?
#6

Super. Actually I do NOT want to allow hand editing on that field. Basically it’s just a file creation date. I was pretty excited to see the on_event handler too so I’d like to also (eventually) create a pre-save hook that looks for the Modified line and updates the date there. I didn’t realize I could pre-fill custom snippet fields like this. Actually quite perfect.

This way, a user preference file can have some basic stuff (author, company, etc), the plugin can fill in the basic stuff and then the user can tab through the file specific stuff.

0 Likes

#7

And quite happy to announce I think I have finished my first non-trivial (i.e. not “hello world!”) plugin routine. Still quite a lot that I want to do, but this seems to work pretty well so far.

class vhdlModeInsertHeaderCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		# Assigning this to a string to keep command shorter later.
		template = "Packages/vhdl-mode/Snippets/vhdl-header.sublime-snippet"
		# Getting a few fields from the settings file
		settings = sublime.load_settings('vhdl-mode.sublime-settings')
		author   = settings.get("vhdl-user", "<user>")
		company  = settings.get("vhdl-company", "<company>")
		date     = time.ctime(time.time())
		year     = time.strftime("%Y",time.localtime())
		# Moving insertion point to the beginning of the file.
		bof = self.view.text_point(0,0)
		self.view.sel().clear()
		self.view.sel().add(sublime.Region(bof))
		self.view.show(bof)
		# Inserting template/snippet
		self.view.run_command("insert_snippet", 
			{
			"name"    : template,
			"AUTHOR"  : author,
			"COMPANY" : company,
			"CDATE"   : date,
			"MDATE"   : date,
			"YEAR"    : year
			})
1 Like

#8

Congratulations and welcome to the wonderful world of sublime plugins. I haven’t tested this, but I think you can change

bof = self.view.text_point(0,0)
self.view.sel().clear()
self.view.sel().add(sublime.Region(bof))
self.view.show(bof)

into

self.view.sel().clear()
self.view.sel().add(sublime.Region(0))
self.view.show(0)

because self.view.text_point(0,0) is always going to be 0.

0 Likes

#9

Yes, I think the point is just the integer offset from the beginning of the file. Mr. Odat noted this earlier and I can add it to my list of improvements to work on as I go along. I’m now delving more deeply into regions as I need it for my next command attempt.

0 Likes

#10

Here’s another idea:

self.view.run_command("move_to", {"to": "bof"})

All the functionality in one line :wink:

5 Likes

#11

Good to know that exists, though I suspect there was value in figuring out how to do it myself. Due to the fact that the corporate-IT-powers-that-be, they’ve bought some sort of blacklist that excludes the entire *.info top level domain for some security reason. Thus, the unofficial documentation is not accessible until I am at home. I’ve been having to deconstruct everything from forum searches, default examples, and the API page and the kindness of replies to my posts (I really am trying to make sure I exhaust my resources before picking brains. I’ve been on the other end before and it is tiresome when Google might have solved things faster than writing the question.)

Eventually I will have to do a pass on my creations to see if there are ways to optimize in either speed, or maintenance value.

0 Likes

#12

The text at the bottom of the unofficial docs where it says e.g. v:latest is something you can click on to open a panel that lets you, among other things, download an offline copy of the documentation in a couple of different formats.

0 Likes

#13

Since the unofficial docs are hosted by readthedocs, you can also access them via their site.

https://sublime-text-unofficial-documentation.readthedocs.io/en/latest/

1 Like

#14

Thanks! That did the trick. I was able to pull up the Read the Docs site without problem. Though for safety I may just download a PDF version as well.

0 Likes