├── .gitignore ├── Default.sublime-commands ├── Default.sublime-keymap ├── DjangoCommands.sublime-settings ├── Main.sublime-menu ├── README.md ├── Side Bar.sublime-menu ├── django-commands.py ├── messages.json ├── messages ├── important.md └── install.md └── snippets ├── ArrayField.sublime-snippet ├── BigIntegerRangeField.sublime-snippet ├── DateRangeField.sublime-snippet ├── DateTimeRangeField.sublime-snippet ├── FloatRangeField.sublime-snippet ├── HStoreField.sublime-snippet └── IntegerRangeField.sublime-snippet /.gitignore: -------------------------------------------------------------------------------- 1 | ###Python### 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | *.pot 48 | 49 | # Django stuff: 50 | *.log 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | # PyBuilder 56 | target/ 57 | 58 | 59 | ###Django### 60 | 61 | *.log 62 | *.pot 63 | *.pyc 64 | local_settings.py 65 | 66 | 67 | ###SublimeText### 68 | 69 | # cache files for sublime text 70 | *.tmlanguage.cache 71 | *.tmPreferences.cache 72 | *.stTheme.cache 73 | 74 | # workspace files are user-specific 75 | *.sublime-workspace 76 | 77 | # project files should be checked into the repository, unless a significant 78 | # proportion of contributors will probably not be using SublimeText 79 | *.sublime-project 80 | 81 | # sftp configuration file 82 | sftp-config.json -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Django: New Project", 4 | "command": "django_new_project" 5 | }, 6 | { 7 | "caption": "Django: New App", 8 | "command": "django_new_app" 9 | }, 10 | { 11 | "caption": "Django: Run Server", 12 | "command": "django_run" 13 | }, 14 | { 15 | "caption": "Django: Run Custom Server", 16 | "command": "django_run_custom" 17 | }, 18 | { 19 | "caption": "Django: Migrate Database", 20 | "command": "django_migrate" 21 | }, 22 | { 23 | "caption": "Django: Boilerplate", 24 | "command": "django_boiler_plate" 25 | }, 26 | { 27 | "caption": "Django: Migrate App", 28 | "command": "django_migrate_app" 29 | }, 30 | { 31 | "caption": "Django: Make Migration", 32 | "command": "django_make_migration" 33 | }, 34 | { 35 | "caption": "Django: List Migrations", 36 | "command": "django_list_migrations" 37 | }, 38 | { 39 | "caption": "Django: Set Virtual Environment", 40 | "command": "set_virtual_env" 41 | }, 42 | { 43 | "caption": "Django: Terminal Here", 44 | "command": "terminal_here" 45 | }, 46 | { 47 | "caption": "Django: Pip Freeze", 48 | "command": "pip_freeze" 49 | }, 50 | { 51 | "caption": "Django: Pip Freeze To File", 52 | "command": "pip_freeze_to_file" 53 | }, 54 | { 55 | "caption": "Django: Pip install Requirements", 56 | "command": "pip_install_requirements" 57 | }, 58 | { 59 | "caption": "Django: Pip Install Packages", 60 | "command": "pip_install_packages" 61 | }, 62 | { 63 | "caption": "Django: Test", 64 | "command": "django_test_app" 65 | }, 66 | { 67 | "caption": "Django: Test All", 68 | "command": "django_test_all" 69 | }, 70 | { 71 | "caption": "Django: Open Docs", 72 | "command": "django_open_docs" 73 | }, 74 | { 75 | "caption": "Django: Search in Docs", 76 | "command": "django_search_docs" 77 | }, 78 | { 79 | "caption": "Django: Set Project Interpreter", 80 | "command": "set_project_interpreter" 81 | }, 82 | { 83 | "caption": "Django: Shell", 84 | "command": "django_shell" 85 | }, 86 | { 87 | "caption": "Django: Help", 88 | "command": "django_help" 89 | }, 90 | { 91 | "caption": "Django: Check", 92 | "command": "django_check" 93 | }, 94 | { 95 | "caption": "Django: Custom Command", 96 | "command": "django_custom" 97 | }, 98 | { 99 | "caption": "Django: Other Command", 100 | "command": "django_other" 101 | }, 102 | { 103 | "caption": "Django: Sql Migration", 104 | "command": "django_sql_migration" 105 | }, 106 | { 107 | "caption": "Django: Use Default Interpreter", 108 | "command": "change_default" 109 | }, 110 | { 111 | "caption": "Django: Click", 112 | "command": "django_click" 113 | }, 114 | { 115 | "caption": "Django: DB Shell", 116 | "command": "django_db_shell" 117 | } 118 | ] 119 | -------------------------------------------------------------------------------- /Default.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+enter"] 4 | , "command": "django_click" 5 | , "context": [{ "key": "selector", "operator": "equal", "operand": "text.html.django" }] 6 | }, 7 | { 8 | "keys": ["ctrl+alt+r"] 9 | , "command": "django_run" 10 | , "context": [{ "key": "selector", "operator": "equal", "operand": "text.html.django" }] 11 | }, 12 | { "keys": ["ctrl+shift+a"] 13 | , "command": "show_overlay" 14 | , "args": {"overlay": "command_palette", "text": "Django:"} }, 15 | ] 16 | -------------------------------------------------------------------------------- /DjangoCommands.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // Path to python interpreter 3 | // It is being overriden when set_virutal_env command is invoked. 4 | "python_bin": null, 5 | // Force finding manage.py in this directory 6 | "django_project_root" : null, 7 | // List of paths where to find virtual environments 8 | "python_virtualenv_paths": [ 9 | "~\\.virtualenvs", // virtualenvwrapper 10 | "~\\.venv", // venv.bash https://github.com/wuub/venv 11 | ], 12 | // Python executable version (in case of using the default) 13 | "python_version": 3, 14 | // Run server host 15 | "server_host": "127.0.0.1", 16 | // Run server in port 17 | "server_port": "8000", 18 | // Linux only: define your preferred terminal emulator 19 | "linux_terminal": "x-terminal-emulator", 20 | // Mac OSX only: define your preferred_terminal_emulator 21 | "osx_terminal": "Terminal", 22 | // Per project setting "python_interpreter" overrides "python_bin" 23 | "project_override": true, 24 | // Open browser pointint to root url after runserver command 25 | "browser_after_runserver": false, 26 | // Example Settings THIS SETTINGS ARE PER PROJECT FILE inside the settings 27 | "server_custom_command": 28 | { 29 | "command": "gunicorn", 30 | "args": 31 | [ 32 | "example.wsgi:application", 33 | "--bind", 34 | "0.0.0.0:8000", 35 | "--workers=3", 36 | "--log-file=-" 37 | ] 38 | }, 39 | //or 40 | "server_custom_command": 41 | { 42 | "command": "uwsgi", 43 | "args": 44 | [ 45 | "--chdir=/path/to/your/project/", 46 | "--module=example.wsgi:application", 47 | "--env", 48 | "DJANGO_SETTINGS_MODULE=example.settings", 49 | "--socket=example.com:8000", 50 | "--processes=5", 51 | "--uid=1000", 52 | "--gid=2000", 53 | "--harakiri=20", 54 | "--max-requests=5000", 55 | "--vacuum" 56 | ], 57 | "run_with_python": false 58 | }, 59 | //or 60 | "server_custom_command": 61 | { 62 | "command": "your_script.sh", 63 | "args": 64 | [], 65 | "run_with_python": false 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "File", 4 | "id": "file", 5 | "children": 6 | [ 7 | {"caption":"New Django project", 8 | "command" : "django_new_project" 9 | } 10 | ] 11 | }, 12 | { 13 | "caption": "Preferences", 14 | "id": "preferences", 15 | "children": 16 | [ 17 | { 18 | "caption": "Package Settings", 19 | "id": "package-settings", 20 | "children": 21 | [ 22 | { 23 | "caption": "Django Manage Commands", 24 | "mnemonic": "d", 25 | "children": 26 | [ 27 | { 28 | "command": "django_side_settings", 29 | "args": {"file": "${packages}/User/DjangoCommands/Default.sublime-keymap", "settings": false}, 30 | "caption": "Key Bindings" 31 | }, 32 | { "caption": "-" }, 33 | { 34 | "command": "django_side_settings", 35 | "args": {"file": "${packages}/User/DjangoCommands.sublime-settings"}, 36 | "caption": "Settings" 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ____ _ __ __ 4 | | _ \(_) __ _ _ __ __ _ ___ | \/ | __ _ _ __ __ _ __ _ ___ 5 | | | | | |/ _` | '_ \ / _` |/ _ \ | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ 6 | | |_| | | (_| | | | | (_| | (_) | | | | | (_| | | | | (_| | (_| | __/ 7 | |____// |\__,_|_| |_|\__, |\___/ |_| |_|\__,_|_| |_|\__,_|\__, |\___| 8 | |__/ |___/ |___/ 9 | ____ _ 10 | / ___|___ _ __ ___ _ __ ___ __ _ _ __ __| |___ 11 | | | / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` / __| 12 | | |__| (_) | | | | | | | | | | | (_| | | | | (_| \__ \ 13 | \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|___/ 14 | 15 | ######################################################################################### 16 | 17 | ## Django manage commands 18 | 19 | ### The best tool for your django development. 20 | 21 | ## Overview 22 | 23 | This tool is a Sublime Text wrapper around django `manage.py` commands. 24 | 25 | ### So you can create new `Django` projects and apps directly from Sublime text like this 26 | 27 | ![new project](http://i.giphy.com/3oKIPjMXcl4xWh4Y8M.gif "New project screen") 28 | 29 | ### Make migrations, migrate the database or run tests 30 | 31 | ![migrations](http://i.giphy.com/3oKIPnnN6HjE0ofhde.gif "make migrations") 32 | 33 | ### And run the test server 34 | ![runserver](http://i.giphy.com/3oKIPdSCp3XDX7Eqze.gif "run server") 35 | 36 | ### Even run tests 37 | 38 | ![Tests](http://i.giphy.com/3oKIPfFrzQvmUj50Ji.gif "tests") 39 | 40 | 41 | You can also run `Django: Custom Command` to access other manage.py commands. Try `Django: Help` and you will get full list of commands provided by each application. 42 | 43 | Or you can choose to use `Django: Other Command` to list and run all commands available to `manage.py` 44 | 45 | ![other](http://i.giphy.com/3oKIPAwltfeuKESVTW.gif "other commands") 46 | 47 | Currently tested on Windows 7/8/10, Mac OS, Ubuntu and many other linux distros. 48 | 49 | Everything tested and running! 50 | 51 | * __Virtualenv support__ 52 | * __Django boilerplate__ 53 | * __Install your dependencies__ 54 | * __Install new pip packages__ 55 | * __Open and search in django documentation from the editor__ 56 | * __Run custom servers__ 57 | * __PostgreSQL specific features snippets__ 58 | * __And More!__ 59 | 60 | ## Installation 61 | 62 | ### Package Control 63 | 64 | The easiest way to install this is with [Package Control](http://wbond.net/sublime\_packages/package\_control). 65 | 66 | * If you just went and installed Package Control, you probably need to restart Sublime Text before doing this next bit. 67 | * Bring up the Command Palette (Command+Shift+p on OS X, Control+Shift+p on Linux/Windows). 68 | * Select "Package Control: Install Package" (it'll take a few seconds) 69 | * Select `Django Manage Commands` when the list appears. 70 | 71 | Package Control will automatically keep plugin up to date with the latest version. 72 | 73 | ## Use 74 | 75 | ### Commands 76 | Currently supports following commands: 77 | 78 | #### Django: 79 | * `Django: New Project` 80 | * `Django: New App` 81 | * `Django: Run Server` 82 | * `Django: Run Custom Server` 83 | * `Django: Boilerplate` 84 | * `Django: Test` 85 | * `Django: Test All` 86 | * `Django: Shell` 87 | * `Django: DB Shell` 88 | * `Django: Custom Command` 89 | * `Django: Other Command` 90 | * `Django: Check` 91 | * `Django: Help` 92 | * `Django: Open Docs` 93 | * `Django: Search in Docs` 94 | * `Django: Make Migration` 95 | * `Django: SQLMigration` 96 | * `Django: Migrate Database` 97 | * `Django: List Migrations` 98 | * `Django: Click` 99 | 100 | #### Virtual Environment: 101 | * `Django: Set Virtual Environment` 102 | * `Django: Terminal Here` 103 | * `Django: Pip Freeze` 104 | * `Django: Pip Freeze To File` 105 | * `Django: Pip Install Packages` 106 | * `Django: Pip Install Requirements` 107 | * `Django: Set Project Interpreter` 108 | * `Django: Use Default Interpreter` 109 | 110 | ### Settings 111 | 112 | * `python_bin`: path to python interpreter 113 | * `python_version` : default python interpreter version 114 | * `python_virtualenv_paths`: list of paths where virtualenvs are located (ex:`~/.virtualenvs/`)\* 115 | * `server_host`: host for the runserver command 116 | * `server_port`: port for the server to listen 117 | * `linux_terminal`: Linux only, used to define a custom command line emulator\*\* 118 | * `osx_terminal`: Mac OSX only, used to define a custom terminal emulator.\*\*\* 119 | * `browser_after_runserver`: set true to open a browser pointing to the root url after a run server command. 120 | * `project_override`: (Boolean) Per project setting "python_interpreter" overrides "python_bin" 121 | * `server_custom_command`: Per project setting to specify a custom server to run 122 | 123 | *** 124 | 125 | #### Each setting has it's own explanation and some examples of use at the default settings file. 126 | 127 | \*It's important to set your envs directories 128 | 129 | \*The folders in this list should be the parent folder of the virtualenv folder, not the virtualenv folder itself 130 | 131 | \*\*Default is `x-terminal-emulator` with a fallback to `xterm` 132 | 133 | \*\*\*Default is `Terminal` 134 | 135 | ### Please report any issue, bug, enhacement or comment [here](https://github.com/vladimirnani/DjangoCommands/issues) 136 | ### We'll be glad to read and work on all of them 137 | 138 | 139 | -------------------------------------------------------------------------------- /Side Bar.sublime-menu: -------------------------------------------------------------------------------- 1 | [{ "caption": "New Django project", "id": "django-new-project", "command": "django_new_project","args": {"paths": []}}] -------------------------------------------------------------------------------- /django-commands.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import threading 4 | import subprocess 5 | import os 6 | import glob 7 | import re 8 | 9 | from ntpath import basename as ntbasename, split as ntsplit 10 | from shutil import which 11 | from platform import system 12 | from functools import partial 13 | from collections import OrderedDict 14 | from urllib.parse import urlencode 15 | 16 | SETTINGS_FILE = 'DjangoCommands.sublime-settings' 17 | PLATFORM = system() 18 | TERMINAL = '' 19 | 20 | 21 | def log(message): 22 | print(' - Django: {0}'.format(message)) 23 | 24 | 25 | class DjangoCommand(sublime_plugin.WindowCommand): 26 | project_true = True 27 | error = False 28 | error_msg = None 29 | 30 | def __init__(self, *args, **kwargs): 31 | self.settings = sublime.load_settings(SETTINGS_FILE) 32 | self.interpreter_versions = {2: "python2", 33 | 3: "python3"} if PLATFORM is not "Windows" else {2: "python", 3: "python"} 34 | sublime_plugin.WindowCommand.__init__(self, *args, **kwargs) 35 | 36 | @property 37 | def startupinfo(self): 38 | if PLATFORM == 'Windows': 39 | startupinfo = subprocess.STARTUPINFO() 40 | startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 41 | return startupinfo 42 | else: 43 | return None 44 | 45 | def get_executable(self): 46 | self.project_true = self.settings.get('project_override') 47 | settings_interpreter = self.settings.get('python_bin') 48 | project = self.window.project_data() 49 | settings_exists = 'settings' in project.keys() 50 | if settings_exists and self.project_true: 51 | project_interpreter = project['settings'].get('python_interpreter') 52 | if project_interpreter is not None and self.project_true is True: 53 | return project_interpreter 54 | elif project_interpreter is not None and self.project_true is False: 55 | return settings_interpreter 56 | else: 57 | version = self.settings.get("python_version") 58 | return which(self.interpreter_versions[version]) 59 | elif settings_interpreter is not None: 60 | return settings_interpreter 61 | else: 62 | version = self.settings.get("python_version") 63 | return which(self.interpreter_versions[version]) 64 | 65 | def get_version(self, extb=None): 66 | if extb is None: 67 | binary = self.get_executable() 68 | else: 69 | binary = extb 70 | command = [binary, '-c', 'import django;print(django.get_version())'] 71 | 72 | try: 73 | output = subprocess.check_output(command, startupinfo=self.startupinfo) 74 | except subprocess.CalledProcessError: 75 | self.error = True 76 | self.error_msg = "No django module was found, check if Django is installed in the current environment" 77 | return 0 78 | else: 79 | version = re.match(r'(\d\.\d+)', output.decode('utf-8')).group(0) 80 | return version 81 | 82 | def find_manage_py(self): 83 | django_project_root = \ 84 | sublime.active_window().active_view().settings().get('django_project_root') \ 85 | or self.settings.get('django_project_root') 86 | for path in [django_project_root] if django_project_root else sublime.active_window().folders(): 87 | for root, dirs, files in os.walk(path): 88 | if 'manage.py' in files: 89 | return os.path.join(root, 'manage.py') 90 | self.error = True 91 | self.error_msg = "manage.py not found, unable to proceed" 92 | return None 93 | 94 | def choose(self, choices, action): 95 | on_input = partial(action, choices) 96 | self.window.show_quick_panel(choices, on_input) 97 | 98 | def go_to_project_home(self): 99 | try: 100 | if self.manage_py is None: 101 | return 102 | except: 103 | return 104 | base_dir = os.path.abspath(os.path.join(self.manage_py, os.pardir)) 105 | if(os.path.exists(base_dir)): 106 | os.chdir(base_dir) 107 | else: 108 | self.error = True 109 | self.error_msg = "Project root not found" 110 | return 111 | 112 | def format_command(self, command): 113 | binary = self.get_executable() 114 | self.manage_py = self.find_manage_py() 115 | self.go_to_project_home() 116 | 117 | command = [binary, self.manage_py] + command.split(' ') 118 | return command 119 | 120 | def define_terminal(self): 121 | global TERMINAL 122 | if PLATFORM != 'Windows': 123 | settings_names = {"Linux": "linux_terminal", "Darwin": "osx_terminal"} 124 | fallbacks = {"Linux": "xterm", "Darwin": "Terminal"} 125 | TERMINAL = self.settings.get(settings_names.get(PLATFORM), fallbacks.get(PLATFORM)) 126 | if not which(TERMINAL, mode=os.F_OK | os.X_OK): 127 | self.error_msg = 'terminal emulator not found: {}\nUsing fallback instead'.format(TERMINAL) 128 | self.display_error_message() 129 | TERMINAL = fallbacks.get(PLATFORM) 130 | 131 | def display_error_message(self): 132 | sublime.error_message(self.error_msg) 133 | self.error = False 134 | 135 | def display_process_error_message(self, process): 136 | outs, err = process.communicate() 137 | if err and err.decode(): 138 | sublime.error_message(err.decode()) 139 | else: 140 | return 141 | 142 | def run_command(self, command): 143 | self.define_terminal() 144 | commands = self.format_command(command) 145 | if self.error: 146 | self.display_error_message() 147 | return 148 | thread = CommandThread(commands) 149 | thread.start() 150 | 151 | 152 | class CommandThread(threading.Thread): 153 | 154 | def __init__(self, command, cwd='.', notsplit=False): 155 | self.command = command 156 | self.notsplit = notsplit 157 | self.cwd = cwd 158 | threading.Thread.__init__(self) 159 | 160 | def run(self): 161 | env = os.environ.copy() 162 | 163 | if PLATFORM == 'Windows': 164 | command = ['cmd.exe', '/k'] 165 | command.extend(list(filter(None, self.command))) 166 | command.extend(['&&', 'timeout', '/T', '10', '&&', 'exit']) 167 | 168 | elif(self.notsplit): 169 | command = "{}".format(' '.join(self.command)) 170 | 171 | else: 172 | command = "{}".format(' '.join([cmd.replace(' ', "\ ") for cmd in self.command])) 173 | 174 | if PLATFORM == 'Linux': 175 | command = [ 176 | TERMINAL, 177 | '-e', 'bash -c \"{0}; read line\"'.format(command) 178 | ] 179 | if PLATFORM == 'Darwin': 180 | command = ["open", "-a", TERMINAL].extend(command) 181 | 182 | log('Command is : {0}'.format(str(command))) 183 | try: 184 | subprocess.Popen(command, env=env, cwd=self.cwd) 185 | except (subprocess.CalledProcessError, ValueError, OSError) as e: 186 | sublime.error_message("{}".format(e)) 187 | 188 | 189 | class DjangoSimpleCommand(DjangoCommand): 190 | command = '' 191 | extra_args = [] 192 | 193 | def get_command(self): 194 | return "{} {}".format(self.command, " ".join(self.extra_args)) 195 | 196 | def run(self): 197 | self.extra_args = [] 198 | command = self.get_command() 199 | self.run_command(command) 200 | 201 | 202 | class DjangoAppCommand(DjangoCommand): 203 | command = '' 204 | extra_args = [] 205 | app_descriptor = 'models.py' 206 | 207 | def find_apps(self): 208 | apps = set() 209 | for project_folder in sublime.active_window().folders(): 210 | dirs = [x[0] for x in os.walk(project_folder)] 211 | for dir in dirs: 212 | dir = os.path.expanduser(dir) 213 | pattern = os.path.join(dir, "*", self.app_descriptor) 214 | apps.update(list(map(lambda x: x, glob.glob(pattern)))) 215 | return sorted(apps) 216 | 217 | def prettify(self, app_dir, base_dir): 218 | name = app_dir.replace(base_dir, '') 219 | name = name.replace(self.app_descriptor, '') 220 | name = name[1:-1] 221 | name = name.replace(os.path.sep, '.') 222 | return name 223 | 224 | def on_choose_app(self, apps, index): 225 | if index == -1: 226 | return 227 | name = apps[index] 228 | self.run_command( 229 | "{} {} {}".format(self.command, 230 | "".join(name), 231 | " ".join(self.extra_args))) 232 | 233 | def run(self): 234 | self.go_to_project_home() 235 | choices = self.find_apps() 236 | self.manage_py = self.find_manage_py() 237 | base_dir = os.path.dirname(self.manage_py) 238 | choices = [self.prettify(path, base_dir) for path in choices] 239 | self.choose(choices, self.on_choose_app) 240 | 241 | 242 | class DjangoOtherCommand(DjangoSimpleCommand): 243 | 244 | def get_commands(self): 245 | command = self.format_command('help --commands') 246 | out = str(subprocess.check_output(command, startupinfo=self.startupinfo)) 247 | out = re.search('b\'(.*)\'', out).group(1) 248 | commands = out.split( 249 | '\\n')[:-1] if PLATFORM is not "Windows" else out.split('\\r\\n')[:-1] 250 | return commands 251 | 252 | def on_choose_command(self, commands, index): 253 | if index == -1: 254 | return 255 | name = commands[index] 256 | self.run_command(name) 257 | 258 | def run(self): 259 | commands = self.get_commands() 260 | self.choose(commands, self.on_choose_command) 261 | 262 | 263 | class DjangoRunCommand(DjangoSimpleCommand): 264 | command = 'runserver' 265 | 266 | def run(self): 267 | port = self.settings.get('server_port', "8000") 268 | host = self.settings.get('server_host', "127.0.0.1") 269 | self.extra_args = [host, port] 270 | inComannd = "{} {}:{}".format(self.command, host, port) 271 | self.run_command(inComannd) 272 | if(self.settings.get('browser_after_runserver', False)): 273 | sublime.set_timeout( 274 | lambda: self.window.run_command('open_url', {'url': 'http://{}:{}'.format(host, port)}), 100) 275 | 276 | 277 | class DjangoRunCustomCommand(DjangoSimpleCommand): 278 | 279 | def get_script(self, executable, script_name): 280 | return os.path.join(os.path.dirname(executable), script_name) 281 | 282 | def run(self): 283 | project = self.window.project_data() 284 | p_settings = 'settings' in project.keys() 285 | self.custom_command = project['settings'].get('server_custom_command') if p_settings else None 286 | self.define_terminal() 287 | executable = self.get_executable() 288 | script = self.custom_command.get('command') 289 | script = script if os.path.exists(script) else self.get_script(executable, script) 290 | commands = [executable, script, " ".join(self.custom_command.get('args'))] if self.custom_command.get( 291 | 'run_with_python', True) else ["", script, " ".join(self.custom_command.get('args'))] 292 | thread = CommandThread("{} {} {}".format(*commands), cwd=os.path.dirname(self.find_manage_py())) 293 | if self.error: 294 | self.display_error_message() 295 | return 296 | else: 297 | thread.start() 298 | 299 | 300 | class DjangoShellCommand(DjangoSimpleCommand): 301 | command = 'shell' 302 | 303 | 304 | class DjangoDbShellCommand(DjangoSimpleCommand): 305 | command = 'dbshell' 306 | 307 | 308 | class DjangoCheckCommand(DjangoSimpleCommand): 309 | command = 'check' 310 | 311 | 312 | class DjangoHelpCommand(DjangoSimpleCommand): 313 | command = 'help' 314 | 315 | 316 | class DjangoMigrateCommand(DjangoSimpleCommand): 317 | command = 'migrate' 318 | 319 | 320 | class DjangoMigrateAppCommand(DjangoAppCommand): 321 | command = 'migrate' 322 | 323 | 324 | class DjangoTestAllCommand(DjangoSimpleCommand): 325 | command = 'test' 326 | 327 | 328 | class DjangoTestAppCommand(DjangoAppCommand): 329 | command = 'test' 330 | app_descriptor = 'tests.py' 331 | 332 | 333 | class DjangoMakeMigrationCommand(DjangoSimpleCommand): 334 | command = 'makemigrations' 335 | 336 | 337 | class DjangoListMigrationsCommand(DjangoSimpleCommand): 338 | command = 'migrate' 339 | extra_args = ['--list'] 340 | 341 | 342 | class DjangoSqlMigrationCommand(DjangoAppCommand): 343 | command = 'sqlmigrate' 344 | 345 | def path_leaf(self, path): 346 | head, tail = ntsplit(path) 347 | return os.path.splitext(tail)[0] or os.path.splitext(ntbasename(head))[0] 348 | 349 | def is_enabled(self): 350 | return True 351 | 352 | def on_choose_migration(self, apps, index): 353 | if index == -1: 354 | return 355 | self.extra_args.append(apps[index]) 356 | self.run_command( 357 | "{} {} {}".format(self.command, self.name, " ".join(self.extra_args))) 358 | 359 | def on_app_selected(self, apps, index): 360 | self.name = apps[index] 361 | path = os.path.join( 362 | os.path.dirname(self.find_apps()[index]), 'migrations') 363 | migrations = [_path for _path in map(self.path_leaf, glob.iglob(os.path.join(path, r'*.py')))] 364 | migrations.remove('__init__') 365 | sublime.set_timeout( 366 | lambda: self.choose(migrations, self.on_choose_migration), 20) 367 | 368 | def run(self): 369 | self.extra_args = [] 370 | self.go_to_project_home() 371 | choices = self.find_apps() 372 | self.manage_py = self.find_manage_py() 373 | base_dir = os.path.dirname(self.manage_py) 374 | choices = [self.prettify(path, base_dir) for path in choices] 375 | self.choose(choices, self.on_app_selected) 376 | 377 | 378 | class DjangoCustomCommand(DjangoCommand): 379 | 380 | def run(self): 381 | caption = "Django manage.py command" 382 | self.window.show_input_panel(caption, '', self.on_done, None, None) 383 | 384 | def on_done(self, command): 385 | command = command 386 | if command.strip() == '': 387 | return 388 | self.run_command(command) 389 | 390 | 391 | class VirtualEnvCommand(DjangoCommand): 392 | command = '' 393 | extra_args = [] 394 | 395 | def is_enabled(self): 396 | return self.settings.get('python_bin') is not None 397 | 398 | def find_virtualenvs(self, venv_paths): 399 | binary = "Scripts" if PLATFORM == 'Windows' else "bin" 400 | venvs = set() 401 | for path in venv_paths: 402 | path = os.path.expanduser(path) 403 | pattern = os.path.join(path, "*", binary, "activate_this.py") 404 | venvs.update(list(map(os.path.dirname, glob.glob(pattern)))) 405 | return sorted(venvs) 406 | 407 | def run(self): 408 | self.define_terminal() 409 | self.manage_py = self.find_manage_py() 410 | self.go_to_project_home() 411 | bin_dir = os.path.dirname(self.settings.get('python_bin')) 412 | command = [os.path.join(bin_dir, self.command)] + self.extra_args 413 | thread = CommandThread(command) 414 | thread.start() 415 | 416 | 417 | class TerminalHereCommand(VirtualEnvCommand): 418 | command = 'activate' 419 | 420 | def run(self): 421 | self.define_terminal() 422 | self.manage_py = self.find_manage_py() 423 | self.go_to_project_home() 424 | bin_dir = os.path.dirname(self.settings.get('python_bin')) 425 | if PLATFORM == 'Windows': 426 | command = ['cmd', '/k', '{}'.format(os.path.join(bin_dir, self.command))] 427 | if PLATFORM == 'Linux' or PLATFORM == 'Darwin': 428 | command = ["bash", "--rcfile", "<(echo '. ~/.bashrc && . {}')".format(os.path.join(bin_dir, self.command))] 429 | thread = CommandThread(command, notsplit=True) 430 | thread.start() 431 | 432 | 433 | class PipFreezeCommand(VirtualEnvCommand): 434 | command = 'pip' 435 | extra_args = ['freeze'] 436 | 437 | 438 | class PipFreezeToFileCommand(VirtualEnvCommand): 439 | command = 'pip' 440 | extra_args = ['freeze'] 441 | 442 | def on_done(self, filename): 443 | self.extra_args.append('>') 444 | self.extra_args.append(filename) 445 | VirtualEnvCommand.run(self) 446 | 447 | def run(self): 448 | self.window.show_input_panel( 449 | "File name", "requirements.txt", self.on_done, None, None) 450 | 451 | 452 | class PipInstallPackagesCommand(VirtualEnvCommand): 453 | command = 'pip' 454 | extra_args = ['install'] 455 | 456 | def appendPackages(self, text): 457 | self.extra_args.append(text) 458 | super(PipInstallPackagesCommand, self).run() 459 | 460 | def run(self): 461 | self.extra_args = ['install'] 462 | self.window.show_input_panel( 463 | 'Packages', '', self.appendPackages, None, None) 464 | 465 | 466 | class PipInstallRequirementsCommand(VirtualEnvCommand): 467 | command = 'pip' 468 | extra_args = ['install', '-r'] 469 | file_name = 'requirements.txt' 470 | 471 | def another_file(self, text): 472 | self.extra_args.append(text) 473 | super(PipInstallRequirementsCommand, self).run() 474 | 475 | def run(self): 476 | self.extra_args = ['install', '-r'] 477 | if os.path.exists(self.file_name): 478 | self.extra_args.append(self.file_name) 479 | super(PipInstallRequirementsCommand, self).run() 480 | else: 481 | sublime.message_dialog('requirements.txt not found') 482 | self.window.show_input_panel( 483 | 'File to install', '', self.another_file, None, None) 484 | 485 | 486 | class SetVirtualEnvCommand(VirtualEnvCommand): 487 | 488 | def is_enabled(self): 489 | return True 490 | 491 | def set_virtualenv(self, venvs, index): 492 | if index == -1: 493 | return 494 | name, directory = venvs[index] 495 | log('Virtual environment "{0}" is set'.format(name)) 496 | binary = os.path.join(directory, 'python') 497 | self.settings.set("python_bin", binary) 498 | sublime.save_settings(SETTINGS_FILE) 499 | 500 | def run(self): 501 | venv_paths = self.settings.get("python_virtualenv_paths", []) 502 | choices = self.find_virtualenvs(venv_paths) 503 | choices = [[path.split(os.path.sep)[-2], path] for path in choices] 504 | self.choose(choices, self.set_virtualenv) 505 | 506 | 507 | class SetProjectInterpreterCommand(VirtualEnvCommand): 508 | 509 | def is_enabled(self): 510 | return True 511 | 512 | def set_project_interpreter(self, venvs, index): 513 | if index == -1: 514 | return 515 | project = self.window.project_data() 516 | if "settings" in project.keys(): 517 | pass 518 | else: 519 | project["settings"] = {} 520 | project["settings"]["python_interpreter"] = os.path.join(venvs[index][1], 'python') 521 | self.window.set_project_data(project) 522 | 523 | def run(self): 524 | venv_paths = self.settings.get("python_virtualenv_paths", []) 525 | choices = self.find_virtualenvs(venv_paths) 526 | choices = [[path.split(os.path.sep)[-2], path] for path in choices] 527 | self.choose(choices, self.set_project_interpreter) 528 | 529 | 530 | class ChangeDefaultCommand(VirtualEnvCommand): 531 | 532 | def use_default(self): 533 | self.settings.erase('python_bin') 534 | sublime.save_settings(SETTINGS_FILE) 535 | 536 | def run(self): 537 | self.use_default() 538 | 539 | 540 | class DjangoClickCommand(sublime_plugin.TextCommand): 541 | TEMPLATE_DIR = 'templates' 542 | 543 | def parse_tag(self, line): 544 | 545 | RE_PARAMS = re.compile(r'(with)|(\w+=[\'"]\w+[\'"])') 546 | 547 | RE_BLOCK = re.compile( 548 | r'.*{%%\s*(?P%s)\s+(?P.+)?[\'"]?\s*%%}' 549 | % '|'.join(['include', 'extends', 'includeblocks'])) 550 | RE_NAMES = re.compile(r'[\'"]([/\.\-_a-zA-Z0-9\s]+)[\'"]') 551 | 552 | line = re.sub(RE_PARAMS, "", line) 553 | 554 | match = re.match(RE_BLOCK, line) 555 | 556 | if match: 557 | targets = re.findall(RE_NAMES, match.groupdict()['names']) 558 | 559 | return match.groupdict()['tag'], targets 560 | 561 | return None, [] 562 | 563 | def run(self, edit): 564 | region = self.view.sel()[0] 565 | line = self.view.line(region) 566 | line_contents = self.view.substr(line) 567 | 568 | tag, targets = self.parse_tag(line_contents) 569 | 570 | if tag: 571 | base, current_file = self.view.file_name().split( 572 | '%(separator)stemplates%(separator)s' % dict( 573 | separator=os.path.sep), 1) 574 | 575 | for one in targets: 576 | tar = os.path.join(base, self.TEMPLATE_DIR, one) 577 | if os.path.isfile(tar): 578 | window = sublime.active_window() 579 | window.open_file(tar, sublime.ENCODED_POSITION) 580 | else: 581 | for root, dirs, filenames in os.walk(base): 582 | for f in filenames: 583 | if f == one: 584 | tar = os.path.join(root, one) 585 | window = sublime.active_window() 586 | if os.path.exists(tar): 587 | window.open_file( 588 | tar, sublime.ENCODED_POSITION) 589 | 590 | 591 | class DjangoBoilerPlate(sublime_plugin.WindowCommand): 592 | options = ['urls', 'models', 'views', 'admin', 'forms', 'tests'] 593 | 594 | def on_done(self, index): 595 | if index < 0: 596 | return 597 | urls = """from django.conf.urls import patterns, include, url 598 | 599 | urlpatterns = [ 600 | # Examples: 601 | # url(r'^$', 'example.views.home', name='home'), 602 | # url(r'^blog/', include(blog.urls)), 603 | ] 604 | """ 605 | admin = """from django.contrib import admin 606 | 607 | # Register your models here. 608 | """ 609 | views = """from django.shortcuts import render 610 | # Create your views here. 611 | """ 612 | models = """from django.db import models 613 | # Define your models here 614 | """ 615 | forms = """from django import forms 616 | # Create your forms here 617 | """ 618 | tests = """from django.test import TestCase 619 | 620 | # Create your tests here. 621 | """ 622 | actions = OrderedDict() 623 | for option in self.options: 624 | actions[option] = eval(option) 625 | text = actions[self.options[index]] 626 | self.view = self.window.active_view() 627 | self.view.run_command('write_helper', {"text": text, "point": 0}) 628 | 629 | def run(self): 630 | self.window.show_quick_panel(self.options, self.on_done) 631 | 632 | 633 | class WriteHelperCommand(sublime_plugin.TextCommand): 634 | 635 | def run(self, edit, point, text): 636 | self.view.insert(edit, point, text) 637 | 638 | 639 | class DjangoNewProjectCommand(SetVirtualEnvCommand): 640 | 641 | def folder_selected(self, index): 642 | self.create_project(directory=self.window.folders()[index]) 643 | 644 | def check_folders(self, name): 645 | if len(self.window.folders()) == 1: 646 | self.create_project(name=name, directory=self.window.folders()[0]) 647 | else: 648 | self.name = name 649 | self.window.show_quick_panel( 650 | self.window.folders(), self.folder_selected) 651 | 652 | def create_project(self, **kwargs): 653 | name = kwargs.get('name') 654 | directory = kwargs.get('directory') 655 | if name is not None: 656 | pass 657 | else: 658 | name = self.name 659 | order = os.path.join( 660 | os.path.abspath(os.path.dirname(self.interpreter)), "django-admin.py") 661 | command = [self.interpreter, order, "startproject", name, directory] 662 | log(command) 663 | process = subprocess.Popen(command, startupinfo=self.startupinfo) 664 | self.display_process_error_message(process) 665 | 666 | def set_interpreter(self, index): 667 | if index == -1: 668 | return 669 | name, self.interpreter = self.choices[index] 670 | if name is not "default": 671 | self.interpreter = os.path.join(self.interpreter, 'python') 672 | if self.get_version(self.interpreter) == 0: 673 | self.error_msg = "No module 'django' found in the selected environment" 674 | self.display_error_message() 675 | return 676 | self.window.show_input_panel( 677 | "Project name", "", self.check_folders, None, None) 678 | 679 | def run(self): 680 | venv_paths = self.settings.get("python_virtualenv_paths", []) 681 | version = self.settings.get("python_version") 682 | envs = self.find_virtualenvs(venv_paths) 683 | self.choices = [[path.split(os.path.sep)[-2], path] for path in envs] 684 | self.choices.append( 685 | ["default", which(self.interpreter_versions[version])]) 686 | sublime.message_dialog( 687 | "Select a python interpreter for the new project") 688 | self.window.show_quick_panel(self.choices, self.set_interpreter) 689 | 690 | 691 | class DjangoNewAppCommand(DjangoSimpleCommand): 692 | command = 'startapp' 693 | 694 | def create_app(self, text): 695 | self.extra_args.append(text) 696 | command = self.format_command(self.get_command()) 697 | log(command) 698 | if self.error: 699 | self.display_error_message() 700 | else: 701 | process = subprocess.Popen(command, env=os.environ.copy(), stderr=subprocess.PIPE, 702 | stdout=subprocess.PIPE, startupinfo=self.startupinfo) 703 | self.display_process_error_message(process) 704 | 705 | def run(self): 706 | if self.get_version() == 0: 707 | self.display_error_message() 708 | return 709 | self.extra_args = list() 710 | self.window.show_input_panel( 711 | "App name", '', self.create_app, None, None) 712 | 713 | 714 | class DjangoOpenDocsCommand(DjangoCommand): 715 | 716 | def run(self): 717 | version = self.get_version() 718 | if version == 0: 719 | self.display_error_message() 720 | return 721 | url = "https://docs.djangoproject.com/en/{}/".format(version) 722 | self.window.run_command('open_url', {'url': url}) 723 | 724 | 725 | class DjangoSearchDocsCommand(DjangoCommand): 726 | 727 | def on_done(self, text): 728 | release = self.get_version() 729 | if release == 0: 730 | self.display_error_message() 731 | return 732 | params = {'q': text} 733 | url = "https://docs.djangoproject.com/en/{}/search/?{}".format( 734 | release, 735 | urlencode(params)) 736 | self.window.run_command('open_url', {'url': url}) 737 | 738 | def run(self): 739 | sel = self.window.active_view().substr(self.window.active_view().sel()[0]) 740 | if(sel is not None): 741 | selection = sel 742 | else: 743 | selection = '' 744 | self.window.show_input_panel('Search:', selection, self.on_done, None, None) 745 | 746 | 747 | class DjangoSideSettingsCommand(sublime_plugin.WindowCommand): 748 | 749 | def run(self, file, settings=True): 750 | self.window.run_command('new_window') 751 | window = sublime.active_window() 752 | window.run_command('open_file', {'file': SETTINGS_FILE if settings else 'Default.sublime-keymap'}) 753 | window.active_view().set_read_only(True) 754 | window.run_command('set_layout', { 755 | "cols": [0.0, 0.5, 1.0], 756 | "rows": [0.0, 1.0], 757 | "cells": [[0, 0, 1, 1], [1, 0, 2, 1]] 758 | }) 759 | window.run_command('open_file', {'file': file}) 760 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "messages/install.md", 3 | "1.7.9": "messages/important.md" 4 | } -------------------------------------------------------------------------------- /messages/important.md: -------------------------------------------------------------------------------- 1 | Django manage commands 1.7.9 2 | 3 | - Fallback for 'linux-terminal' setting with a dash is dropped, now is essential to use 'linux_terminal' setting with and underscore. 4 | 5 | - Linux extended compatibility dropping 'gnome-terminal' in favor of 'x-terminal-emulator' as the default 'linux_terminal' setting and with 'xterm' as a fallback. 6 | 7 | - Easier access to user settings and keybindings, now showing both default and user defined files side by side to ease the definition of user settings. 8 | 9 | - Mac OSX users now can select their preferred terminal emulator to be triggered to run the corresponding command. Use `osx_terminal` setting. 10 | 11 | - Windows users, would you like to be able to select another terminal instead of cmd.exe like mintty, cmder or git-bash? If so please open an Issue(only one please so others can comment on it, and keep the issue tracker organized) at our github repo and let us know. 12 | 13 | - Windows slight performance optimization. 14 | 15 | - Minor fixes. -------------------------------------------------------------------------------- /messages/install.md: -------------------------------------------------------------------------------- 1 | ____ _ __ __ 2 | | _ \(_) __ _ _ __ __ _ ___ | \/ | __ _ _ __ __ _ __ _ ___ 3 | | | | | |/ _` | '_ \ / _` |/ _ \ | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ 4 | | |_| | | (_| | | | | (_| | (_) | | | | | (_| | | | | (_| | (_| | __/ 5 | |____// |\__,_|_| |_|\__, |\___/ |_| |_|\__,_|_| |_|\__,_|\__, |\___| 6 | |__/ |___/ |___/ 7 | ____ _ 8 | / ___|___ _ __ ___ _ __ ___ __ _ _ __ __| |___ 9 | | | / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` / __| 10 | | |__| (_) | | | | | | | | | | | (_| | | | | (_| \__ \ 11 | \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|___/ 12 | 13 | ######################################################################################### 14 | 15 | 16 | - Ctrl + Shift + A: opens a menu with all Django commands. 17 | 18 | - Remember to set the path to the directory where your virtual environments are installed in "python_virtualenv_paths" for the plugin to work correclty, as many directories as you need can be added. 19 | 20 | - Enjoy and please let us know if you run into any issue. 21 | 22 | :smile: -------------------------------------------------------------------------------- /snippets/ArrayField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psarray 5 | 6 | source.python 7 | ArrayField 8 | 9 | -------------------------------------------------------------------------------- /snippets/BigIntegerRangeField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psbigintrange 5 | 6 | source.python 7 | BigIntegerRangeField 8 | 9 | -------------------------------------------------------------------------------- /snippets/DateRangeField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psdaterange 5 | 6 | source.python 7 | DateRangeField 8 | 9 | -------------------------------------------------------------------------------- /snippets/DateTimeRangeField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psdatimerange 5 | 6 | source.python 7 | DateTimeRangeField 8 | 9 | -------------------------------------------------------------------------------- /snippets/FloatRangeField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psflrange 5 | 6 | source.python 7 | FloatRangeField 8 | 9 | -------------------------------------------------------------------------------- /snippets/HStoreField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pshstore 5 | 6 | source.python 7 | HStoreField 8 | 9 | -------------------------------------------------------------------------------- /snippets/IntegerRangeField.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psintrange 5 | 6 | source.python 7 | IntegerRangeField 8 | 9 | --------------------------------------------------------------------------------