Sublime Forum

Pass a function to a TextCommand plugin?

#1

Hey guys,

I’m trying to write a generic plugin that performs arbitrary operations on your current selection in sublimetext. The usage I had in mind was that youd have a globals alias to the command, which is a function that accepts a function as a parameter – that function itself accepting and returning a single string. Example:

selmod(string.upper)

or

selmod(lambda s: str(len(s)))

Writing the plugin itself was simple, but for some reason when I try to pass a lambda or other function as a keyword argument, it claims its None. Heres my code:

class Selmod(sublime_plugin.TextCommand): def run(self, edit, func=None): for region in self.view.sel(): text = self.view.substr(region) self.view.replace(edit, region, func(text))

But when I try to invoke it from the console, i get this:

>>> view.run_command("selmod", {"func": lambda s: 'test'}) Traceback (most recent call last): File "./sublime_plugin.py", line 276, in run_ File "./selrep.py", line 9, in run self.view.replace(edit, region, func(text)) TypeError: 'NoneType' object is not callable

Any ideas?

0 Likes

#2

I believe that all the data you send to a command gets transmuted and must be representable as JSON. In such a case a lambda would probably turn into a None… :frowning:

0 Likes

#3

As far as I know adzenith is right. I actually run different arbitrary functions in the BracketHighlighter plugin.

If I want BracketHighlighter to do something extra, I just send in the command with the parameters in a json structure.
The plugin parameter is where I contain the info to run the arbitrary function or class (specifically the “command” and “args” items). You can see the command is broken up: filename.classname

	{
		"caption": "Bracket Highlighter: Show Left Bracket",
		"command": "bracket_highlighter_key",
		"args":
		{
			"lines" : true,
			"plugin":
			{
				"type": "quote","bracket","tag"],
				"command": "bracketselect.select_bracket",
				"args": {"select": "left"}
			}
		}
	},

In my case I just created my own class to be run from my plugin

[code]import bracket_plugin
import sublime

Move cursor to left or right bracket if specified, else select the content between

class select_bracket(bracket_plugin.BracketPluginCommand):
def run(self, bracket, content, selection, select=’’):
(first, last) = (content.a, content.b)
if select == ‘left’:
last = content.a
elif select == ‘right’:
first = content.b
self.attr.set_selection([sublime.Region(first, last)])[/code]

The only thing else I needed was a simple layer to process the parameters, dynamically import the file and function, and execute it. I had to decide what the main code would generally supply the external functions, but that was it. Modularizing things like this allows me to quickly add in new stuff that doesn’t really need to be in the core code or even distributed with the core code; it also helps with maintainability.

I am sure there may be more eloquent ways to possibly do this; I have very little Python experience. But basically, what your trying to do is certainly doable.

0 Likes

#4

I think I’ve got a solution, although its very hacky. I pass my function to the command as a string, using s as the text selection variable. The command looks like this:

class Selmod(sublime_plugin.TextCommand): def run(self, edit, func=None): exec('def f(s): %s;' % func) for region in self.view.sel(): newtext = f(self.view.substr(region)) self.view.replace(edit, region, newtext)

And it works by running it from the command line using view.run_command(). Success!

Now the next step is to figure out the best way to pass this function to the command. Is it possible to use sublimetext’s nice popup menu to accept user input and use that as a parameter for a command?

0 Likes

#5

Oh, I see what you are doing now. You are trying to allow dynamic input from the user.

Glad you got it working. I haven’t personally looked into accepting input directly from the user, but I am sure there has to be a way though that somewhere here knows.

0 Likes

#6

Honestly, even if i can somehow register a global in the python environment so i could just open up a console, and type mod(‘return s.upper().strip()’); would suffice, doesnt need to be pretty. But i cant even figure out how to do that. At least my command works, though

0 Likes

#7

You can create a simple file called helloworld.py

and put the simple function inside.

def test(value): print "hello" print value

and then from command line you can say ST2 command line say

from helloworld import test

and then you can issue the command

test("world")

and you will get

hello world

for the rest of your session that function will be accessible from command line. I am just not sure yet how to make it accessible without manually issuing the from X import Y command.

0 Likes

#8

Hmm, right, but how do I access the view object in helloworld.py? The issue is this separation of two layers

0 Likes

#9

Just make sure helloworld.py imports sublime

And use

sublime.active_window().active_view()

0 Likes

#10

Spent about 15 minutes doing a little research. This opens an input panel and takes a string. It then displays says hello to whatever input you give it in the status bar.

[code]import sublime
import sublime_plugin

def display_hello(value):
sublime.active_window().active_view().set_status(“hello”, "Hello " + value + “!”)

class HelloWorldCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.window().show_input_panel(
“Hello:”,
“World”,
display_hello,
None,
None
)
[/code]

0 Likes

#11

[quote=“facelessuser”]Spent about 15 minutes doing a little research. This opens an input panel and takes a string. It then displays says hello to whatever input you give it in the status bar.

[code]import sublime
import sublime_plugin

def display_hello(value):
sublime.active_window().active_view().set_status(“hello”, "Hello " + value + “!”)

class HelloWorldCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.window().show_input_panel(
“Hello:”,
“World”,
display_hello,
None,
None
)
[/code][/quote]

Thanks man! I’ve got it working. I may package this up and post it on announcements…but then again maybe not, its very hacky and dangerous because it uses exec()

0 Likes