├── .gitignore ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── extract_to_blade.sublime-commands ├── .travis.yml ├── Preferences.sublime-settings ├── appveyor.yml ├── LICENSE ├── extract2blade.py ├── extract_to_blade_command.py ├── README.md └── tests └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/* 2 | tests/__pycache__/* 3 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // { "keys": ["super+alt+e"], "command": "extract_to_blade"}, 3 | ] -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // { "keys": ["super+alt+e"], "command": "extract_to_blade"}, 3 | ] -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // { "keys": ["ctrl+alt+e"], "command": "extract_to_blade"}, 3 | ] -------------------------------------------------------------------------------- /extract_to_blade.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Extract: Move selection to blade view ...", 4 | "command": "extract_to_blade" 5 | } 6 | ] -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 3.3 4 | - 3.4 5 | - 3.5 6 | - 3.6 7 | - 3.7-dev 8 | 9 | script: 10 | - python -m unittest tests/test.py 11 | -------------------------------------------------------------------------------- /Preferences.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "extract_to_blade_save_last_path": true, 3 | "extract_to_blade_relative_path": false, 4 | "extract_to_blade_include_sentence": "@include('%s')" 5 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | 3 | matrix: 4 | - PYTHON: "C:\\Python33" 5 | - PYTHON: "C:\\Python33-x64" 6 | DISTUTILS_USE_SDK: "1" 7 | - PYTHON: "C:\\Python34" 8 | - PYTHON: "C:\\Python34-x64" 9 | DISTUTILS_USE_SDK: "1" 10 | - PYTHON: "C:\\Python35" 11 | - PYTHON: "C:\\Python35-x64" 12 | - PYTHON: "C:\\Python36" 13 | - PYTHON: "C:\\Python36-x64" 14 | 15 | install: 16 | - "%PYTHON%\\python.exe -m pip install wheel" 17 | 18 | build: off 19 | 20 | test_script: 21 | - "%PYTHON%\\python.exe -m unittest tests/test.py" 22 | 23 | artifacts: 24 | # bdist_wheel puts your built wheel in the dist directory 25 | - path: dist\* 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mohannad Najjar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /extract2blade.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | class ExtractToBlade(object): 5 | def get_output_directory(path, filename): 6 | filename = filename.replace('.blade.php','') 7 | return ExtractToBlade.output_paths(path, filename)['output_directory'] 8 | 9 | def get_absolute_filepath(path, filename): 10 | filename = filename.replace('.blade.php','') 11 | output_paths = ExtractToBlade.output_paths(path, filename) 12 | blade_paths = ExtractToBlade.blade_paths(output_paths['abspath'], filename, output_paths['basename']) 13 | return os.path.join( output_paths['output_directory'] , blade_paths['filename'] ) 14 | 15 | def get_blade_filepath(path, filename): 16 | filename = filename.replace('.blade.php','') 17 | output_paths = ExtractToBlade.output_paths(path, filename) 18 | return ExtractToBlade.blade_paths(output_paths['abspath'], filename, output_paths['basename'])['path'] 19 | 20 | def get_blade_dirpath(path, filename): 21 | filename = filename.replace('.blade.php','') 22 | output_paths = ExtractToBlade.output_paths(path, filename) 23 | return ExtractToBlade.blade_paths(output_paths['abspath'], filename, output_paths['basename'])['user_dirpath'] 24 | 25 | def output_paths(sublime_file_path, filename): 26 | abspath = os.path.normpath(os.path.join(sublime_file_path, filename.strip('//').strip('\\'))) 27 | 28 | basename = os.path.basename(abspath) 29 | 30 | if '.' in basename: 31 | abspath = os.path.join( os.path.normpath(abspath.replace(basename,'') ) , basename.replace('.','/')) 32 | basename = os.path.basename(abspath) 33 | 34 | 35 | output_directory = os.path.normpath(os.path.dirname(abspath)) 36 | 37 | return {"abspath": abspath, "basename": basename, "output_directory": output_directory} 38 | 39 | def blade_paths(abspath, filename, basename): 40 | blade_path = ExtractToBlade.resolve_blade_path(abspath) 41 | # save how the last time the user entered the path to the blade view 42 | # by ripping off the resolved basename from the input 43 | blade_user_dirpath = ExtractToBlade.rreplace(filename, basename, '',1) 44 | 45 | blade_filename = basename + '.blade.php' 46 | 47 | return {"path": blade_path, "user_dirpath": blade_user_dirpath, "filename": blade_filename} 48 | # replace last occurrence of string https://stackoverflow.com/a/2556156/4330182 49 | def rreplace(s, old, new, count): 50 | return (s[::-1].replace(old[::-1], new[::-1], count))[::-1] 51 | 52 | def blade_views_dir(absolute_path): 53 | absolute_path = absolute_path.lower().replace('\\','/').split('resources/views',1) 54 | path_to_views = absolute_path[0] + 'resources/views/' 55 | 56 | try: 57 | path_after_views = absolute_path[1][1:] # remove first character 58 | except IndexError: 59 | path_after_views = "" 60 | 61 | return { "path_to_views" : path_to_views, "path_after_views" : path_after_views } 62 | 63 | def resolve_blade_path(abspath): 64 | blade_filename = ExtractToBlade.blade_views_dir(abspath)['path_after_views'] 65 | blade_filename = blade_filename.replace('/', '.') 66 | return blade_filename 67 | -------------------------------------------------------------------------------- /extract_to_blade_command.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin 2 | import os 3 | from time import sleep 4 | 5 | from .extract2blade import ExtractToBlade 6 | 7 | class ExtractToBladeCommand(sublime_plugin.TextCommand): 8 | def run(self, edit): 9 | self.init() 10 | 11 | if not self.allow_relative_path: 12 | self.sublime_file_path = ExtractToBlade.blade_views_dir(self.sublime_file_path)['path_to_views'] 13 | 14 | if self.save_last_path: 15 | self.default_blade_input = self.project_data.get('extract2blade_last_blade_path','') 16 | 17 | # If multiple selection, join it by a new line separator 18 | for region in self.view.sel(): 19 | if not region.empty(): 20 | self.text_selected = self.text_selected + self.view.substr(region) + '\n' 21 | 22 | # Remove last new line formatter 23 | self.text_selected = ExtractToBlade.rreplace(self.text_selected,'\n','',1) 24 | 25 | # Open the new blade file input panel 26 | self.window.show_input_panel( 27 | ('File name (in %s): (suffix: .blade.php)' % (self.sublime_file_path)), 28 | self.default_blade_input, # Default value in input 29 | self.append_to_file, # Method to execute after input 30 | None, # No 'change' handler 31 | self.cancel_handler 32 | ) 33 | 34 | # If the feature is enabled, Cancel handler 35 | # will reset the last blade path 36 | def cancel_handler(self): 37 | if self.save_last_path: 38 | self.store_user_dirpath('') 39 | 40 | def append_to_file(self, input_filename): 41 | output_directory = ExtractToBlade.get_output_directory(self.sublime_file_path, input_filename) 42 | absolute_file_path = ExtractToBlade.get_absolute_filepath(self.sublime_file_path, input_filename) 43 | blade_filepath = ExtractToBlade.get_blade_filepath(self.sublime_file_path, input_filename) 44 | blade_dirpath = ExtractToBlade.get_blade_dirpath(self.sublime_file_path, input_filename) 45 | 46 | # Create the directory for the extracted new file if not exist 47 | self.create_dir(output_directory) 48 | 49 | # Add or append the text to the file 50 | self.write_to_file(absolute_file_path, self.text_selected) 51 | 52 | # Remove the original text 53 | self.view.run_command('left_delete') 54 | 55 | # Insert include sentence 56 | self.insert_include_sentence(blade_filepath) 57 | 58 | # Display the file, after appending to it 59 | file_view = self.window.open_file(absolute_file_path) 60 | 61 | # Keep the view open 62 | self.window.focus_view(self.view) 63 | 64 | # If enabled, save how the user entered the path to the file 65 | if self.save_last_path: 66 | self.store_user_dirpath(blade_dirpath) 67 | 68 | def init(self): 69 | self.init_defaults() 70 | self.load_settings() 71 | 72 | def init_defaults(self): 73 | self.window = self.view.window() 74 | self.sublime_vars = self.window.extract_variables() 75 | self.project_data = self.window.project_data() 76 | self.sublime_file_path = self.sublime_vars['file_path'] 77 | self.default_blade_input = '' 78 | # extract the text 79 | self.text_selected = '' 80 | 81 | def load_settings(self): 82 | sublime_preferences = sublime.load_settings('Preferences.sublime-settings') 83 | self.save_last_path = sublime_preferences.get('extract_to_blade_save_last_path', True) 84 | self.allow_relative_path = sublime_preferences.get('extract_to_blade_relative_path', False) 85 | self.include_sentence = sublime_preferences.get('extract_to_blade_include_sentence', "@include('%s')") 86 | 87 | def create_dir(self, directory): 88 | if not os.path.exists(directory): 89 | os.makedirs(directory) 90 | 91 | def write_to_file(self, absolute_file_path, text): 92 | f = open(absolute_file_path, 'a', encoding="utf-8") 93 | f.write(text) 94 | f.close() 95 | 96 | def store_user_dirpath(self, user_dirpath): 97 | proj_data = self.project_data 98 | proj_data['extract2blade_last_blade_path'] = user_dirpath 99 | self.window.set_project_data(proj_data) 100 | 101 | def insert_include_sentence(command_instance, blade_path): 102 | include_sentence = (command_instance.include_sentence % (blade_path)) 103 | 104 | # Create the blade "include" sentence 105 | command_instance.view.run_command('insert', {"characters": include_sentence }) 106 | 107 | # Hide Autocomplete 108 | command_instance.view.run_command('insert', {"characters": '\n'}) 109 | command_instance.view.run_command("hide_auto_complete") 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extract Text to Laravel Blade View 2 | 3 | [![Build Status](https://travis-ci.org/MohannadNaj/sublime-extract-to-blade.svg?branch=master)](https://travis-ci.org/MohannadNaj/sublime-extract-to-blade) [![Build status](https://ci.appveyor.com/api/projects/status/txtal1v37kjs31xl?svg=true)](https://ci.appveyor.com/project/MohannadNaj/sublime-extract-to-blade) 4 | 5 | 6 | This plugin for Sublime Text 3 allows you to extract the selected text into a new or existing [laravel blade views](https://laravel.com/docs/master/blade) with the `@include` sentence and the appropriate path. 7 | 8 | ![screenshot](https://i.imgur.com/53e2Kgd.gif) 9 | 10 | ## 💾 Installation 11 | 12 | ### Package Control 13 | 14 | It is highly recommended to install `Extract Text to Laravel Blade View` with [Package Control](https://packagecontrol.io). 15 | 16 | 1. [Install Package Control](https://packagecontrol.io/installation) if you haven't yet. 17 | 2. Open the command palette (Ctrl+Shift+P for Windows/Linux, Cmd+Shift+P for Mac OS) 18 | 3. Search for _Package Control: Install Package_ and hit Enter. 19 | 4. Type `Extract Text to Laravel Blade View` and press Enter to install it. 20 | 21 | 22 | ### Manual Installation 23 | 24 | You can clone this repository into your _Sublime Text 3/Packages_ 25 | 26 | ```shell 27 | git clone https://github.com/MohannadNaj/sublime-extract-to-blade 28 | ``` 29 | 30 | ##### Mac OS 31 | 32 | ```shell 33 | cd ~/Library/Application Support/Sublime Text 3/Packages/ 34 | git clone git://github.com/MohannadNaj/sublime-extract-to-blade.git 35 | ``` 36 | 37 | 38 | ##### Linux 39 | 40 | ```shell 41 | cd ~/.config/sublime-text-3/Packages 42 | git clone git://github.com/MohannadNaj/sublime-extract-to-blade.git 43 | ``` 44 | 45 | 46 | ##### Windows 47 | 48 | ```shell 49 | cd "%APPDATA%\Sublime Text 3\Packages" 50 | git clone git://github.com/MohannadNaj/sublime-extract-to-blade.git 51 | ``` 52 | 53 | 👉 The `git` command must be available on the command line. 54 | 55 | 👉 You may need to add the directory containing `git.exe` to your `PATH` environment variable. 56 | 57 | 58 | ## Usage 59 | 60 | - While on a Laravel Blade View, Select some text 61 | - open the Command Palette (Ctrl+Shift+P for Windows/Linux, Cmd+Shift+P for Mac OS), and look for "Extract: Move selection to blade view ..". 62 | > Alternatively, you can set a key map for this step. see [Keymap](#keymap) 63 | 64 | - Enter the blade path for the new blade view (e.g: `welcome.services`) 65 | > you can use relative blade paths also if it's enabled. see [Options](#options) 66 | 67 | The plugin will create or append the extracted text to the newly created blade view, and insert `@include` statement in the text source file. The plugin will look for the `resources/views/` directory to resolve the entered path. 68 | 69 | ## Options 70 | Set your options by navigating to _Preferences > Settings_. 71 | 72 | ### extract_to_blade_save_last_path 73 | _default: true_ 74 | ``` js 75 | { 76 | "extract_to_blade_save_last_path": true 77 | } 78 | ``` 79 | By enabling this option, the plugin will remember how you located the directory of the blade file the last time you extracted text, and will set the next input to the same directory. 80 | 81 | If you extracted a portion of the text to `welcome.about`, the next time the input will be ready for you by default: `welcome.`. 82 | 83 | ### extract_to_blade_relative_path 84 | _default: false_ 85 | ``` js 86 | { 87 | "extract_to_blade_relative_path": false 88 | } 89 | ``` 90 | By enabling this option, the plugin will build the blade paths relatively to the text source file path. 91 | 92 | If you extracted text from `resources/views/layouts/app.blade.php` and typed: `header`, it will create the file on `resources/views/layouts/`, so the blade path for it will be: `@include('layouts.header')`. 93 | 94 | Disable this if you used to write the full blade path at your `include` statements. 95 | 96 | 97 | ### extract_to_blade_include_sentence 98 | _default: @include('%s')_ 99 | ``` js 100 | { 101 | "extract_to_blade_include_sentence": "@include('%s')" 102 | } 103 | ``` 104 | The sentence will be used for entering the resolved blade path. 105 | 106 | Change this if you prefer the double quotes over the apostrophe for example. 107 | ``` js 108 | { 109 | // double quotes + 4 lines after!! 110 | "extract_to_blade_include_sentence": "@include(\"%s\")\n\n\n\n" 111 | } 112 | ``` 113 | 114 | ## Keymap 115 | 116 | You can set the keymap for extracting the selected text to a Laravel blade view from _Preferences > Key Bindings_. Assuming you want Ctrl+Alt+E as the command shortcut: 117 | ``` js 118 | [ 119 | // windows 120 | { "keys": ["ctrl+alt+e"], "command": "extract_to_blade"}, 121 | 122 | // Linux and Mac OS 123 | { "keys": ["super+alt+e"], "command": "extract_to_blade"}, 124 | ] 125 | ``` 126 | 127 | ## Credits 128 | 129 | - The forked [Extract Text to File](https://github.com/dreki/sublime-extract-to-file) plugin 130 | - The [Installation](#installation) part on this README was modified from [GitGutter's README file](https://github.com/jisaacks/GitGutter/blob/master/README.md) 131 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('../') 3 | 4 | from extract2blade import ExtractToBlade 5 | 6 | import unittest 7 | import os 8 | # https://stackoverflow.com/a/652299/4330182 9 | class Test(object): 10 | def __new__(cls, **attrs): 11 | result = object.__new__(cls) 12 | result.__dict__ = attrs 13 | return result 14 | 15 | class TestPathsMethods(unittest.TestCase): 16 | def test_1(self): 17 | tests = [ 18 | Test( 19 | input="welcome", 20 | path=os.path.normpath('/path/to/resources/views/'), 21 | output_directory=os.path.normpath('/path/to/resources/views/'), 22 | absolute_filepath=os.path.normpath('/path/to/resources/views/welcome.blade.php'), 23 | blade_filepath="welcome", 24 | blade_dirpath="", 25 | ), 26 | Test( 27 | input="wel/foo/bar", 28 | path=os.path.normpath('/path/to/resources/views/'), 29 | output_directory=os.path.normpath('/path/to/resources/views/wel/foo'), 30 | absolute_filepath=os.path.normpath('/path/to/resources/views/wel/foo/bar.blade.php'), 31 | blade_filepath="wel.foo.bar", 32 | blade_dirpath="wel/foo/", 33 | ), 34 | Test( 35 | input="wel.foo.bar", 36 | path=os.path.normpath('/path/to/resources/views/'), 37 | output_directory=os.path.normpath('/path/to/resources/views/wel/foo'), 38 | absolute_filepath=os.path.normpath('/path/to/resources/views/wel/foo/bar.blade.php'), 39 | blade_filepath="wel.foo.bar", 40 | blade_dirpath="wel.foo.", 41 | ), 42 | Test( 43 | input="wel/FOo.bar", 44 | path=os.path.normpath('/path/to/aresources/viewS/'), 45 | output_directory=os.path.normpath('/path/to/aresources/viewS/wel/FOo'), 46 | absolute_filepath=os.path.normpath('/path/to/aresources/viewS/wel/FOo/bar.blade.php'), 47 | blade_filepath="wel.foo.bar", 48 | blade_dirpath="wel/FOo.", 49 | ), 50 | Test( 51 | input="/moh/FOo.bar", 52 | path=os.path.normpath('/path/to/aresources/viewS/'), 53 | output_directory=os.path.normpath('/path/to/aresources/viewS/moh/FOo'), 54 | absolute_filepath=os.path.normpath('/path/to/aresources/viewS/moh/FOo/bar.blade.php'), 55 | blade_filepath="moh.foo.bar", 56 | blade_dirpath="/moh/FOo.", 57 | ), 58 | Test( 59 | input="\\hello/foo.bar", 60 | path=os.path.normpath('/path/to/aresources/viewS/'), 61 | output_directory=os.path.normpath('/path/to/aresources/viewS/hello/foo'), 62 | absolute_filepath=os.path.normpath('/path/to/aresources/viewS/hello/foo/bar.blade.php'), 63 | blade_filepath="hello.foo.bar", 64 | blade_dirpath="\\hello/foo.", 65 | ), 66 | Test( 67 | input="../hello/foo.bar", 68 | path=os.path.normpath('/path/to/resources/views/relative/'), 69 | output_directory=os.path.normpath('/path/to/resources/views/hello/foo'), 70 | absolute_filepath=os.path.normpath('/path/to/resources/views/hello/foo/bar.blade.php'), 71 | blade_filepath="hello.foo.bar", 72 | blade_dirpath="../hello/foo.", 73 | ), 74 | Test( 75 | input="hello/foo.bar.hi", 76 | path=os.path.normpath('/path/to/resources/views/'), 77 | output_directory=os.path.normpath('/path/to/resources/views/hello/foo/bar'), 78 | absolute_filepath=os.path.normpath('/path/to/resources/views/hello/foo/bar/hi.blade.php'), 79 | blade_filepath="hello.foo.bar.hi", 80 | blade_dirpath="hello/foo.bar.", 81 | ), 82 | Test( 83 | input="../../welcome.modal", 84 | path=os.path.normpath('/path/to/resources/views/shared/layouts'), 85 | output_directory=os.path.normpath('/path/to/resources/views/welcome'), 86 | absolute_filepath=os.path.normpath('/path/to/resources/views/welcome/modal.blade.php'), 87 | blade_filepath="welcome.modal", 88 | blade_dirpath="../../welcome.", 89 | ), 90 | Test( 91 | input="../../welcome.partials.modal", 92 | path=os.path.normpath('/path/to/resources/views/shared/layouts'), 93 | output_directory=os.path.normpath('/path/to/resources/views/welcome/partials'), 94 | absolute_filepath=os.path.normpath('/path/to/resources/views/welcome/partials/modal.blade.php'), 95 | blade_filepath="welcome.partials.modal", 96 | blade_dirpath="../../welcome.partials.", 97 | ), 98 | Test( 99 | input="header", 100 | path=os.path.normpath('/path/to/resources/views/layouts/'), 101 | output_directory=os.path.normpath('/path/to/resources/views/layouts'), 102 | absolute_filepath=os.path.normpath('/path/to/resources/views/layouts/header.blade.php'), 103 | blade_filepath="layouts.header", 104 | blade_dirpath="", 105 | ), 106 | Test( 107 | input="../header", 108 | path=os.path.normpath('/path/to/resources/_views/layouts/'), 109 | output_directory=os.path.normpath('/path/to/resources/_views/'), 110 | absolute_filepath=os.path.normpath('/path/to/resources/_views/header.blade.php'), 111 | blade_filepath="", # Can't resolve blade path! 112 | blade_dirpath="../", 113 | ), 114 | ] 115 | 116 | for test in tests: 117 | output_directory = ExtractToBlade.get_output_directory(test.path, test.input) 118 | absolute_filepath = ExtractToBlade.get_absolute_filepath(test.path, test.input) 119 | blade_filepath = ExtractToBlade.get_blade_filepath(test.path, test.input) 120 | blade_dirpath = ExtractToBlade.get_blade_dirpath(test.path, test.input) 121 | if not test.output_directory == False: 122 | self.assertEqual(output_directory, test.output_directory) 123 | if not test.absolute_filepath == False: 124 | self.assertEqual(absolute_filepath, test.absolute_filepath) 125 | if not test.blade_filepath == False: 126 | self.assertEqual(blade_filepath, test.blade_filepath) 127 | if not test.blade_dirpath == False: 128 | self.assertEqual(blade_dirpath, test.blade_dirpath) 129 | --------------------------------------------------------------------------------