Skip to content

Instantly share code, notes, and snippets.

@kkiernan
Last active December 27, 2022 09:25
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kkiernan/bc562611cbc8bb3c9b62 to your computer and use it in GitHub Desktop.
Save kkiernan/bc562611cbc8bb3c9b62 to your computer and use it in GitHub Desktop.
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
Copy link

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

@kkiernan
Copy link
Author

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.

@kkiernan
Copy link
Author

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

@kkiernan
Copy link
Author

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.

@erichard
Copy link

What keybinding do you use ?

@kkiernan
Copy link
Author

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

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

@s4wny
Copy link

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.

@kkiernan
Copy link
Author

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