├── showtime └── phoenix_beagle.gif ├── Default.sublime-commands ├── Default.sublime-keymap ├── README.md ├── PhoenixBeagle.sublime-settings └── phoenix_beagle.py /showtime/phoenix_beagle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noma4i/sublime-phoenix-beagle/HEAD/showtime/phoenix_beagle.gif -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { "caption": "Show More Phoenix Files", "command": "phoenix_beagle" }, 3 | { "caption": "Phoenix Migrations List", "command": "phoenix_beagle", 4 | "args": { "command": "mlist"} 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /Default.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["command+shift+k"], 4 | "command": "phoenix_beagle" 5 | }, 6 | { 7 | "keys": ["command+shift+o"], 8 | "command": "phoenix_beagle", 9 | "args": { "command": "mlist"} 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sublime Text Phoenix Framework Beagle 2 | 3 | Sublime Text plugin for better Phoenix Framework experience. 4 | 5 | **For support of v1.2 checkout branch `phoenix_v1.2`** 6 | 7 | ### Quick Start 8 | - Clone/download plugin to your SublimeText User folder under SublimePhoenixBeagle 9 | 10 | ### Functions 11 | 12 | - Jump from controllers to corresponding views/templates 13 | - View tests from controllers 14 | - Jump back to controller from views 15 | - List Migrations 16 | 17 | ##### Configure 18 | 19 | ``` 20 | [ 21 | { "caption": "Show More Phoenix Files", "command": "phoenix_beagle" }, 22 | { "caption": "Phoenix Migrations List", "command": "phoenix_beagle", 23 | "args": { "command": "mlist"} 24 | } 25 | ] 26 | ``` 27 | 28 | You can override default shortcuts: 29 | 30 | ``` 31 | [ 32 | { 33 | "keys": ["command+shift+k"], 34 | "command": "phoenix_beagle" 35 | }, 36 | { 37 | "keys": ["command+shift+o"], 38 | "command": "phoenix_beagle", 39 | "args": { "command": "mlist"} 40 | } 41 | ] 42 | ``` 43 | 44 | 45 | ### Showtime! 46 | ![Query](showtime/phoenix_beagle.gif?raw=true) 47 | -------------------------------------------------------------------------------- /PhoenixBeagle.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "patterns": { 3 | // Controllers 4 | ".+\/web\/controllers\/(.+)_controller.ex$": [ 5 | "lib/*/web/views/$1_view.ex", 6 | "lib/*/web/templates/$1/**", 7 | "lib/*/web/models/$1.ex", 8 | "lib/*/web/router.ex", 9 | "test/controllers/$1_controller_test.exs" 10 | ], 11 | // Views 12 | ".+\/web\/views\/(.+)_view.ex$": [ 13 | "test/views/$1_view_test.exs", 14 | "lib/*/web/templates/$1/**", 15 | "lib/*/web/controllers/$1_controller.ex", 16 | ], 17 | // Templates 18 | ".+\/web\/templates\/(.+)\/[^\/].+": [ 19 | "lib/*/web/controllers/$1_controller.ex", 20 | "lib/*/web/views/$1_view.ex", 21 | "lib/*/web/models/$1.ex", 22 | "test/views/$1_view_test.exs" 23 | ], 24 | // Models 25 | ".+\/web\/models\/(.+).ex$": [ 26 | "test/models/$1_test.exs", 27 | "lib/*/web/views/$1_view.ex", 28 | "lib/*/web/controllers/$1_controller.ex", 29 | "lib/*/web/templates/$1/**" 30 | ], 31 | // Unit Tests 32 | ".+\/test\/controllers\/(.+)_controller_test.ex": [ 33 | "lib/*/web/controllers/$1_controller.ex" 34 | ], 35 | ".+\/test\/models\/(.+)_test.ex": [ 36 | "lib/*/web/models/$1.ex" 37 | ], 38 | ".+\/test\/views\/(.+)_view_test.exs": [ 39 | "lib/*/web/views/$1_view.ex" 40 | ], 41 | // Migrations 42 | ".+beagle_migrations": [ 43 | "priv/repo/migrations/**" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /phoenix_beagle.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin, os, re, glob, itertools, sys 2 | 3 | class Phoenix(object): 4 | def __init__(self, file_path, patterns, folders): 5 | self.__file_path = file_path 6 | self.__patterns = patterns 7 | self.__root = self.__root(folders) 8 | self.__files = [] 9 | self.__descriptions = [] 10 | self.__build() 11 | 12 | def descriptions(self): 13 | return self.__descriptions 14 | 15 | def files(self): 16 | return self.__files 17 | 18 | def __build(self): 19 | files = set() 20 | file_path = self.__to_posixpath(self.__file_path) 21 | 22 | for regex, paths in list(self.__patterns.items()): 23 | match = re.compile(regex).match(file_path) 24 | if match: 25 | files.update(self.__files_for_paths(regex, match, paths)) 26 | 27 | files = list(files) 28 | if 'migrations' in file_path: 29 | files.sort(reverse=True) 30 | else: 31 | files.sort() 32 | 33 | self.__files = files 34 | self.__descriptions = [self.__file_without_root(file) for file in files] 35 | 36 | def __root(self, folders): 37 | for folder in folders: 38 | if self.__file_path.startswith(os.path.join(folder, "")): 39 | return folder 40 | 41 | def __files_for_paths(self, regex, match, paths): 42 | paths = [self.__replaced_path(match, path) for path in paths] 43 | files = [glob.iglob(os.path.join(self.__root, self.__to_posixpath(path))) for path in paths] 44 | flattened = [self.__to_posixpath(path) for path in list(itertools.chain.from_iterable(files))] 45 | 46 | return flattened 47 | 48 | def __file_without_root(self, file): 49 | clean_path = file[len(self.__root):] 50 | if 'migrations' in clean_path: 51 | clean_path = clean_path[len('/priv/repo/migrations/'):] 52 | return clean_path 53 | 54 | def __replaced_path(self, match, path): 55 | replaced_path = path 56 | for i, group in enumerate(match.groups()): 57 | replaced_path = replaced_path.replace("$%s" % (i + 1), group) 58 | return replaced_path 59 | 60 | def __to_posixpath(self, path): 61 | return re.sub("\\\\", "/", path) 62 | 63 | class PhoenixBeagleCommand(sublime_plugin.WindowCommand): 64 | def run(self, index=None, **args): 65 | command = args.get('command') 66 | active_file_path = self.__active_file_path() 67 | active_folders = sublime.active_window().folders() 68 | if command == 'mlist': 69 | self.__beagle = Phoenix(active_file_path + 'beagle_migrations', self.__patterns(), active_folders) 70 | self.window.show_quick_panel(self.__beagle.descriptions(), self.__open_file) 71 | else: 72 | if not active_folders: 73 | active_folders = os.path.split(active_file_path)[0] 74 | sublime.error_message("SideBar is empty!") 75 | if active_file_path: 76 | if self.__patterns(): 77 | self.__beagle = Phoenix(active_file_path, self.__patterns(), active_folders) 78 | self.window.show_quick_panel(self.__beagle.descriptions(), self.__open_file) 79 | else: 80 | self.__status_msg("Patterns are not loaded!") 81 | else: 82 | self.__status_msg("No open files") 83 | 84 | def __open_file(self, index): 85 | if index >= 0: 86 | self.window.open_file(self.__beagle.files()[index]) 87 | else: 88 | self.__status_msg("No files found") 89 | 90 | def __patterns(self): 91 | return sublime.load_settings("PhoenixBeagle.sublime-settings").get('patterns') 92 | 93 | def __active_file_path(self): 94 | if self.window.active_view(): 95 | file_path = self.window.active_view().file_name() 96 | 97 | if file_path and len(file_path) > 0: 98 | return file_path 99 | 100 | def __status_msg(self, message): 101 | sublime.status_message(message) 102 | --------------------------------------------------------------------------------