Create a gist now

Instantly share code, notes, and snippets.

@kkiernan /create.py
Last active Jan 21, 2016

What would you like to do?
import sublime, sublime_plugin, re
class CreateCommand(sublime_plugin.TextCommand):
# Runs the plugin
def run(self, edit, stub, offset):
self.view.insert(edit, offset, stub)
import sublime, sublime_plugin
class ImplementCommand(sublime_plugin.TextCommand):
# Gets the currently selected symbol
#
def get_selected_symbol(self):
point = self.view.sel()[0]
region = self.view.word(point)
return self.view.substr(region)
# Gets files with names that match the currently selected symbol
#
def get_matching_files(self):
window = self.view.window()
selected_symbol = self.get_selected_symbol()
locations = window.lookup_symbol_in_index(selected_symbol)
files = []
for location in locations:
files.append(location[0])
return files
# Handles the selection of a quick panel item
#
def on_done(self, index):
if index == -1:
return
self.view.run_command("parse", {"path": self.files[index]})
# Runs the plugin
#
def run(self, edit):
self.files = self.get_matching_files()
if (len(self.files) == 1):
self.view.run_command("parse", {"path": self.files[0]})
if (len(self.files) > 1):
self.view.window().show_quick_panel(self.files, self.on_done)
import sublime, sublime_plugin, re
class ParseCommand(sublime_plugin.TextCommand):
# Normalizes a given path to the current system style
# -- This method is from the PHP Companion utils file
#
def normalize_to_system_style_path(self, path):
if sublime.platform() == "windows":
path = re.sub(r"/([A-Za-z])/(.+)", r"\1:/\2", path)
path = re.sub(r"/", r"\\", path)
return path
# Runs the plugin
#
def run(self, edit, path):
# Get the contents of the file at the given path
with open(self.normalize_to_system_style_path(path), "r") as f:
content = f.read()
# Get the methods from the content
self.methods = re.findall("(?<!\* )(?:abstract )?(?:public|protected|private)(?: static)? function [A-z0-9]*\([A-z0-9$=, ]*\)[A-z :]*", content)
self.methods.insert(0, 'Insert all methods')
# Show the available methods in the quick panel
if (len(self.methods) > 0):
self.view.window().show_quick_panel(self.methods, self.on_done)
# Handles selection of a quick panel item
#
def on_done(self, index):
if index == -1:
return
# Find the closing brackets. We'll place the method
# stubs just before the last closing bracket.
closing_brackets = self.view.find_all("[}]")
# Add the method stub(s) to the current file
region = closing_brackets[-1]
point = region.end() - 1
template = "\n\t{0}\n\t{{\n\t\tthrow new \Exception('Method not implemented');\n\t}}\n"
# Better way to handle add all selection?
if index == 0:
for method in self.methods[1:]:
method_stub = template.format(method)
self.view.run_command("create", {"stub": method_stub, "offset": point})
else:
method_stub = template.format(self.methods[index])
self.view.run_command("create", {"stub": method_stub, "offset": point})

erichard commented Aug 4, 2015

Checkout my fork. Instead of inserting all methods I let the user choose the method he wants. Also I think the findall method on line 40 a bit too simple.

It doesn't match theses methods :

  • public static function myStaticMethod()
  • protected function myProtectedMethod()

But will match function statement in comments :

/**
 * public function notARealMethod()
 */

Not sure we can do all with regexes but I will try...

Owner

kkiernan commented Aug 4, 2015

Looks awesome! I like being able to select the methods. That works better for abstract classes anyway, which I forgot about at first. I'll play around with a better regex too.

Also, this one doesn't do anything if more than one file is found. I'll tinker around on your fork and share it then.

Owner

kkiernan commented Aug 4, 2015

Here is a long winded regex that does a bit better: https://regex101.com/r/yD6nY7/7.

Edit (newer version with unit tests): https://regex101.com/r/yD6nY7/12

Owner

kkiernan commented Aug 4, 2015

Take a look at the updated gist when you have some time. It now supports multiple files, like if you are working with a lot of packages that have some overlapping names.

I also tweaked the regex and it seems to be working pretty nicely. It accommodates a mix of abstract, static, and other methods with various visibility. I also tested it with type hinting and return type declarations since I think that has been approved for PHP7.

Updated: Also just added an "Insert all methods" option as well. For interfaces, this would be useful if you have an empty class that you are just starting to work on.

What keybinding do you use ?

Owner

kkiernan commented Aug 18, 2015

I am using f1 at the moment. It runs implement.py to kick everything off.

{ "keys": ["f1"], "command": "implement" }

s4wny commented Dec 4, 2015

This seems like a really useful script, could you turn it into a plugin and upload it to package control? It would be much easier to install then and easier for people to find.

Owner

kkiernan commented Jan 8, 2016

Glad you think it looks useful, thanks for the feedback! I was hoping we could add this to PHP Companion (erichard's awesome plugin) instead of making a standalone plugin.

Right now I use it by downloading a zip of this gist and dropping it into my packages folder. I'm on Windows so the directory is C:\Users\USERNAME\AppData\Roaming\Sublime Text 3\Packages. Not sure what it is on OS X or Linux. Then you need to add a keybinding like the one I mentioned in the comment above to run the command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment