Sublime Forum

Build System for Selected Lines Containing Code

#1

Hi,

I am trying to set up sublime text 2 ( MacOS Yosemite) to run selected lines of a .srp file ( serpent codes). Serpent is A Real-Time Language for Music and Animation: https://www.cs.cmu.edu/~music/aura/serpent-info.htm.

The files are basically .srp files that are excuated using the command : serpent64 sample.srp. So the following sublime-build file works fine.

{
"cmd": ["/Users/csarami/bin/serpent64", "${file}"],
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
"selector": "source.serpent64"
}

I used the plugin mentioned here:

import sublime, sublime_plugin

class ExecuteSelectedCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        for region in self.view.sel():  
            if not region.empty():  
                with open('/a/temporary/file', 'w') as f:
                    f.write(self.view.substr(region))
                self.view.window().run_command('build')
                break

However, above plugin builds the full document and not the selected region. As a newbie I used the page below to create the plugin. I created the file containing the region, however, it builds the whole file.

http://sublimetext.info/docs/en/extensibility/plugins.html

Any help would be greatly appreciated.

CS

1 Like

Execute programs with parameters from text line?
#2

The build system that you specify above is using ${file} so that it always operates on the current file (when that file is a serpent file). So, even though the plugin code is writing the selection to a temporary file, the build command is still trying to build the current file (and not the file that it just wrote), which is why it’s not doing what you want.

In order to do something like this you need to be able to tell the build that it needs to build a different file when you want to run the selection.

For example, your build system could look like this:

{
	"cmd": ["cat", "${file}"],
	"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
	"selector": "text.plain",

	"variants": 
	[
		{
			"name": "Selection",
			"cmd": ["cat", "/tmp/plain_selection.txt"]
		}

	]
}

This tells Sublime that by default it should cat the current file (only if it’s plain text), but also provides a variant named Selection that modifies the command to operation on a hard coded temporary file name instead,

Next, a modified version of the plugin code might look something like this. This version only saves and runs the first selection (expanding it to be a full line). If desired you could of course do what the code above is doing and write out each selection and run it one at a time if that makes more sense for the language you’re using.

import sublime, sublime_plugin

class RunSelectionCommand(sublime_plugin.WindowCommand):
	def run(self):
		# Get the current view in this window
		view = self.window.active_view()

		# Make sure the selection is full lines.
		view.run_command("expand_selection", {"to": "line"})

		# Get the first selection and write it to a file
		sel = view.sel()[0]
		with open("/tmp/plain_selection.txt", 'w') as f:
			f.write(view.substr(sel))

		# Run the build now			
		self.window.run_command("build", {"variant": "Selection"})
		

Now there is a command called run_selection that saves the currently selected text to a tempoary text file and then executes the build, telling Sublime to use the variant instead.

The easiest way to take advantage of this is via a key binding so you can just select some text and away you go. This example uses Ctrl+Alt+B, and ensures that it will only take effect when there is a single selection, since the plugin code above will ignore any selections other than the first.

{ "keys": ["ctrl+alt+b"], "command": "run_selection", "context":
	[
		{ "key": "num_selections", "operator": "equal", "operand": 1 }
	] 
}
3 Likes

#3

Thank you very much. I followed your method. However, it seems It does not create the temp file. When I issue: view.run_command(“runselection”), nothing happens.

Here is how I have adopted your method:

// Sublime Text - Build System for Serpent
{
	"cmd": ["/Users/<user>/bin/serpent64", "${file}"],
	"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
	"selector": "source.serpent64",

	"variants": 
	[
		{
			"name": "Selection",
			"cmd": ["/Users/<user>/bin/serpent64", "/Users/<user>/serpent/temp/file.srp"]
		}

	]
}

And the plugin. Shouldn’t the class inherit from sublime_plugin.TextCommand?

import sublime, sublime_plugin
class RunSelectionCommand(sublime_plugin.WindowCommand): 
	def run(self):
		# Get the current view in this window
		view = self.window.active_view()

		# Make sure the selection is full lines.
		view.run_command("expand_selection", {"to": "line"})

		# Get the first selection and write it to a file
		sel = view.sel()[0]
		with open('/Users/<user>/serpent/temp/file.srp', 'w') as f:
			f.write(view.substr(sel))

		# Run the build now			
		self.window.run_command("build", {"variant": "Selection"})

Again thank you very much for everything and key binding!

1 Like

#4

You may need to make sure that the build system is selected as the current build system in the Tools menu. Assuming that’s not the case, does the Sublime console show any error messages? Aside of that the only other obvious thing I can think of is a missing directory or permission problem that stops it from creating the file. It worked for me when I tested it.

Note however that the command name is run_selection, not runselection, so that would also cause it to not work, assuming that’s actually what you tried.

In this case it doesn’t really matter and it’s more of a style/preference thing; either way would work, though the method for each is slightly different.

TextCommand is generally for commands that want to modify the contents of the buffer in some way; they get a special edit token provided to their run method that allows them to use API functions that change the contents of the buffer. Since they’re for working with text, TextCommand instances have an instance variable named view that represents the view that they’re applied to, and as a result there is a new instance of each one created for every open file.

In contrast WindowCommand is for more general purpose sorts of commands. They’re specific to windows in the same general way that TextCommand is specific to a file, so they have an instance variable named window that represents the window they’re attached to, and a new instance is created for every open window.

The difference in the code is pretty much just that WindowCommand needs to use self.window.active_view() to get the currently active view to work with, while TextCommand could use self.view instead and still do the same stuff otherwise.

2 Likes

#5

Thank you so much. It finally worked. I am so happy!

The issue was for using runselection! So, it there are more than one word in the command name, they should be separated by _ ( underscores)!

Now, there is a minor issue. When I compile the buggy code in MacOS terminal, it loads the debugger! How come, it does not run the debugger when I build it in ST2?

Here is a simple code cotaining bug:

load "debug"
def bug(a): # b is not defined
	print a+b 

def main():
	bug(3)
main()

CS

2 Likes

#6

I’m not familiar with that language or tool. Is there some extra command line argument or environment variable that has to be set to tell the debugger to run?

2 Likes

#7

I don’t think so. Just like Python that packages/modules used are imported using say: import math.
Debug is a module that is loaded at the top of each program. The debugger is enabled by:

require "debug"
//the rest of serpent code ...

The debugger uses special characters to implement colored text in the messages. It can be disable by

require "debug"
term_has_color = false

So there is no switch or extra command. If the program throw a run time error, it called Serpent’s debug module. Like I said if in terminal I issue the buggy file mayflies.srp:

$ serpent64 myfie.srp

It displays the following:

$ serpent64 temp.srp*
load_path /SPCode
debug.srp created srp.log for error report...
runtime exception handler called
exception is: global variable is unbound - a
frame variables: {}
frame pc: 1
frame method: bug
frame class: nil

Debugger invoked.
-----------------------------------------------------
Method <global> in class nil
-----------------------------------------------------
Method <callin_method> in class nil
-----------------------------------------------------
Method <immediate command 0> in class nil, char 57 in file 
/Users/csarami/Downloaded Course/Computer Music Systems and Information Processing/Class Code/tst2.srp
   8: main()
      ^
-----------------------------------------------------
Method main in class nil, char 50 in file 
/Users/csarami/Downloaded Course/Computer Music Systems and Information Processing/Class Code/tst2.srp
   7: 	bug()
      ^
-----------------------------------------------------
Method bug in class nil, char 30 in file 
/Users/csarami/Downloaded Course/Computer Music Systems and Information Processing/Class Code/tst2.srp
   4: 	print a+b
         ^
global variable is unbound - a

Thank you very much again for your help.

1 Like