├── .gitignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── images └── icon.png ├── package.json ├── src ├── Resolver.js ├── classes.js └── extension.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 10 | "stopOnEntry": false 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "php-namespace-resolver" extension will be documented in this file. 3 | 4 | ## [1.1.8] - 2019-05-13 5 | ### Fixed 6 | - Cannot read property 'document' of undefined [#61](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/#61) 7 | 8 | ## [1.1.7] - 2019-03-18 9 | ### Added 10 | - Support multiple autoload paths for namespace generation [#54](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/#54) 11 | 12 | ## [1.1.6] - 2019-03-13 13 | ### Changed 14 | - Replace namespace if already present in file while generating namespace [#50](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/#50) 15 | 16 | ## [1.1.5] - 2019-01-29 17 | ### Fixed 18 | - Trail only the latest double backslash in namespaceBase [#48](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/#48) 19 | - Support windows path [#47](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/#47) 20 | 21 | ## [1.1.4] - 2019-01-29 22 | ### Added 23 | - Auto highlight settings added [#43](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/43) 24 | 25 | ## [1.1.3] - 2019-01-25 26 | ### Added 27 | - Add a new `Generate namespace for this file` command [#42](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/42) 28 | 29 | ## [1.1.2] - 2019-01-18 30 | ### Added 31 | - Add a new `Highlight Not Imported Classes` command [#38](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/38) 32 | - Add a new `Highlight Not Used Classes` command [#39](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/39) 33 | 34 | ## [1.1.1] - 2019-01-11 35 | ### Added 36 | - Add a new `Import All Classes` command [#36](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/36) 37 | 38 | ## [1.1.0] - 2018-06-24 39 | ### Changed 40 | - Adjust words 41 | 42 | ## [1.0.9] - 2018-05-21 43 | ### Added 44 | - Add natural sorting option [#29](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/29) 45 | 46 | ## [1.0.7] - 2018-05-10 47 | ### Added 48 | - New config for auto sorting when a file is saved 49 | 50 | ## [1.0.6] - 2018-04-26 51 | ### Fixed 52 | - Escaping from alias import box not working 53 | - No class is selected warning is not showing 54 | 55 | ## [1.0.5] - 2018-03-24 56 | ### Changed 57 | - Don't move cursor after expanding class 58 | - Don't wait before autosorting and replacing selected class 59 | 60 | ## [1.0.3] - 2018-03-24 61 | ### Fixed 62 | - Add alphabetical fallback when imports are the same length [#25](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/25) 63 | 64 | ## [1.0.2] - 2018-02-20 65 | ### Added 66 | - Allow replacing use statement [#24](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/24) 67 | 68 | ## [1.0.1] - 2018-01-28 69 | ### Fix 70 | - Expand command is not working 71 | 72 | ## [1.0.0] - 2018-01-28 73 | ### Added 74 | - Add built-in php class 75 | ### Changed 76 | - Do not import classes that does not exists [#20](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/20) 77 | 78 | ## [0.9.9] - 2018-01-19 79 | ### Fix 80 | - Extension is not working when namespace is in the first line alongside the php tag [#19](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/19) 81 | 82 | ## [0.9.8] - 2018-01-09 83 | ### Fix 84 | - Extension isnot working on other tab 85 | 86 | ## [0.9.7] - 2018-01-07 87 | ### Added 88 | - Automatically replace selected class when alias is chosen 89 | 90 | ## [0.9.4] - 2018-01-04 91 | ### Added 92 | - Bring back the context menus 93 | 94 | ## [0.9.4] - 2018-01-04 95 | ### Added 96 | - Add multi cursor support 97 | - Import class directly if fqcn is selected 98 | 99 | ## [0.9.3] - 2018-01-04 100 | ### Added 101 | - Check conflict for aliases also 102 | - If class is not found then import class as global class 103 | - Import class at the top of the file if php tag not found 104 | 105 | ## [0.9.2] - 2018-01-02 106 | ### Fixed 107 | - Showing class on picker even if it has a namespace 108 | 109 | ## [0.9.0] - 2018-01-02 110 | ### Added 111 | - Add support to find classes without namespaces 112 | ### Removed 113 | - Remove context menus 114 | 115 | ## [0.8.0] - 2017-10-16 116 | ### Added 117 | - Add support for expanding on multiple cursor 118 | ### Changed 119 | - Check for namespace conflict case insensitively 120 | 121 | ## [0.7.9] - 2017-10-2 122 | ### Changed 123 | - Clean up settings doc 124 | 125 | ## [0.7.8] - 2017-10-2 126 | ### Changed 127 | - Show app namespace on top while choosing namespace to import 128 | 129 | ## [0.7.7] - 2017-09-30 130 | ### Added 131 | - Added support for `interface` `abstract` `trait` keywords [#9](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/9) 132 | - Added support for `final` keyword 133 | ### Changed 134 | - Optimize performance [#10](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/10) 135 | 136 | ## [0.7.6] - 2017-09-06 137 | ### Changed 138 | - Make alphabetic sort in case sensitive. [#2](https://github.com/MehediDracula/PHP-Namespace-Resolver/pull/2) 139 | - Show error message instead of information if nothing to sort 140 | - Bug fixes 141 | 142 | ## [0.7.4] - 2017-09-01 143 | ### Changed 144 | - Move context menu to a separate group 145 | 146 | ## [0.7.3] - 2017-09-01 147 | ### Changed 148 | - Activate context menu only on PHP language 149 | 150 | ## [0.7.0] - 2017-09-01 151 | ### Changed 152 | - Change default keybindings 153 | 154 | ## [0.6.0] - 2017-08-29 155 | ### Added 156 | - Add configuration to exclude files 157 | - If a class with the same name is already imported prompt for an alias 158 | 159 | ### Changed 160 | - Rename command titles 161 | - Rename `messagesOnStatusBar` configuration to `showMessageOnStatusBar` 162 | - Show error message instead of info if the class is already imported 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mehedi Hassan 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Namespace Resolver 2 | 3 | [![Latest Release](https://vsmarketplacebadge.apphb.com/version-short/MehediDracula.php-namespace-resolver.svg 4 | )](https://marketplace.visualstudio.com/items?itemName=MehediDracula.php-namespace-resolver) [![Installs](https://vsmarketplacebadge.apphb.com/installs-short/MehediDracula.php-namespace-resolver.svg 5 | )](https://marketplace.visualstudio.com/items?itemName=MehediDracula.php-namespace-resolver) [![Rating](https://vsmarketplacebadge.apphb.com/rating-short/MehediDracula.php-namespace-resolver.svg)](https://marketplace.visualstudio.com/items?itemName=MehediDracula.php-namespace-resolver#review-details) 6 | 7 | PHP Namespace Resolver can import and expand your class. You can also sort your imported classes by line length or in alphabetical order. 8 | 9 | ## Demo 10 | 11 | ![](https://i.imgur.com/upEGtPa.gif) 12 | 13 | ## Commands 14 | 15 | Search these commands by the title on command palette. 16 | 17 | ```javascript 18 | [ 19 | { 20 | "title": "Import Class", 21 | "command": "namespaceResolver.import" 22 | }, 23 | { 24 | "title": "Import All Classes", 25 | "command": "namespaceResolver.importAll" 26 | }, 27 | { 28 | "title": "Expand Class", 29 | "command": "namespaceResolver.expand" 30 | }, 31 | { 32 | "title": "Sort Imports", 33 | "command": "namespaceResolver.sort" 34 | }, 35 | { 36 | "title": "Highlight Not Imported Classes", 37 | "command": "namespaceResolver.highlightNotImported" 38 | }, 39 | { 40 | "title": "Highlight Not Used Classes", 41 | "command": "namespaceResolver.highlightNotUsed" 42 | }, 43 | { 44 | "title": "Generate namespace for this file", 45 | "command": "namespaceResolver.generateNamespace" 46 | } 47 | ] 48 | ``` 49 | 50 | ## Settings 51 | 52 | You can override these default settings according to your needs. 53 | 54 | ```javascript 55 | { 56 | "namespaceResolver.exclude": "**/node_modules/**", // Exclude glob pattern while finding files 57 | "namespaceResolver.showMessageOnStatusBar": false, // Show message on status bar instead of notification box 58 | "namespaceResolver.autoSort": true, // Auto sort after imports 59 | "namespaceResolver.sortOnSave": false, // Auto sort when a file is saved 60 | "namespaceResolver.sortAlphabetically": false, // Sort imports in alphabetical order instead of line length 61 | "namespaceResolver.sortNatural": false, // Sort imports using a 'natural order' algorithm 62 | "namespaceResolver.leadingSeparator": true, // Expand class with leading namespace separator 63 | "namespaceResolver.highlightOnSave": false, // Auto highlight not imported and not used when a file is saved 64 | "namespaceResolver.highlightOnOpen": false // Auto highlight not imported and not used when a file is opened 65 | } 66 | ``` 67 | 68 | ## Keybindings 69 | 70 | You can override these default keybindings on your `keybindings.json`. 71 | 72 | ```javascript 73 | [ 74 | { 75 | "command": "namespaceResolver.import", 76 | "key": "ctrl+alt+i", 77 | "when": "editorTextFocus" 78 | }, 79 | { 80 | "command": "namespaceResolver.importAll", 81 | "key": "ctrl+alt+a", 82 | "when": "editorTextFocus" 83 | }, 84 | { 85 | "command": "namespaceResolver.expand", 86 | "key": "ctrl+alt+e", 87 | "when": "editorTextFocus" 88 | }, 89 | { 90 | "command": "namespaceResolver.sort", 91 | "key": "ctrl+alt+s", 92 | "when": "editorTextFocus" 93 | }, 94 | { 95 | "command": "namespaceResolver.highlightNotImported", 96 | "key": "ctrl+alt+n", 97 | "when": "editorTextFocus" 98 | }, 99 | { 100 | "command": "namespaceResolver.highlightNotUsed", 101 | "key": "ctrl+alt+u", 102 | "when": "editorTextFocus" 103 | }, 104 | { 105 | "command": "namespaceResolver.generateNamespace", 106 | "key": "ctrl+alt+g", 107 | "when": "editorTextFocus" 108 | } 109 | ] 110 | ``` 111 | 112 | ## Author 113 | 114 | - [@MehediDracula](https://twitter.com/MehediDracula) 115 | 116 | ## License 117 | 118 | [MIT](LICENSE) License. 119 | 120 | Copyright (c) 2017 Mehedi Hassan 121 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MehediDracula/PHP-Namespace-Resolver/232adcac255e2f4180ba0ae280813b832aa6f6c4/images/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-namespace-resolver", 3 | "displayName": "PHP Namespace Resolver", 4 | "description": "Import and expand php namespaces", 5 | "version": "1.1.9", 6 | "publisher": "MehediDracula", 7 | "author": "Mehedi Hasan ", 8 | "license": "SEE LICENSE IN LICENSE", 9 | "engines": { 10 | "vscode": "^1.68.0" 11 | }, 12 | "categories": [ 13 | "Other" 14 | ], 15 | "keywords": [ 16 | "php", 17 | "namespace", 18 | "class", 19 | "import", 20 | "expand" 21 | ], 22 | "galleryBanner": { 23 | "color": "#282c34", 24 | "theme": "dark" 25 | }, 26 | "activationEvents": [ 27 | "onLanguage:php", 28 | "onCommand:namespaceResolver.import", 29 | "onCommand:namespaceResolver.importAll", 30 | "onCommand:namespaceResolver.expand", 31 | "onCommand:namespaceResolver.sort", 32 | "onCommand:namespaceResolver.highlightNotImported", 33 | "onCommand:namespaceResolver.highlightNotUsed", 34 | "onCommand:namespaceResolver.generateNamespace" 35 | ], 36 | "main": "./src/extension", 37 | "icon": "images/icon.png", 38 | "contributes": { 39 | "menus": { 40 | "editor/context": [ 41 | { 42 | "when": "resourceLangId == php", 43 | "command": "namespaceResolver.import", 44 | "alt": "namespaceResolver.import", 45 | "group": "0_namespace_resolver@1" 46 | }, 47 | { 48 | "when": "resourceLangId == php", 49 | "command": "namespaceResolver.importAll", 50 | "alt": "namespaceResolver.importAll", 51 | "group": "0_namespace_resolver@2" 52 | }, 53 | { 54 | "when": "resourceLangId == php", 55 | "command": "namespaceResolver.expand", 56 | "alt": "namespaceResolver.expand", 57 | "group": "0_namespace_resolver@3" 58 | }, 59 | { 60 | "when": "resourceLangId == php", 61 | "command": "namespaceResolver.sort", 62 | "alt": "namespaceResolver.sort", 63 | "group": "0_namespace_resolver@4" 64 | }, 65 | { 66 | "when": "resourceLangId == php", 67 | "command": "namespaceResolver.highlightNotImported", 68 | "alt": "namespaceResolver.highlightNotImported", 69 | "group": "0_namespace_resolver@5" 70 | }, 71 | { 72 | "when": "resourceLangId == php", 73 | "command": "namespaceResolver.highlightNotUsed", 74 | "alt": "namespaceResolver.highlightNotUsed", 75 | "group": "0_namespace_resolver@6" 76 | }, 77 | { 78 | "when": "resourceLangId == php", 79 | "command": "namespaceResolver.generateNamespace", 80 | "alt": "namespaceResolver.generateNamespace", 81 | "group": "0_namespace_resolver@7" 82 | } 83 | ] 84 | }, 85 | "configuration": { 86 | "type": "object", 87 | "title": "PHP Namespace Resolver extension configuration", 88 | "properties": { 89 | "namespaceResolver.exclude": { 90 | "type": "string", 91 | "default": "**/node_modules/**", 92 | "description": "Exclude glob pattern while finding files" 93 | }, 94 | "namespaceResolver.showMessageOnStatusBar": { 95 | "type": "boolean", 96 | "default": false, 97 | "description": "Show message on status bar instead of notification box" 98 | }, 99 | "namespaceResolver.autoSort": { 100 | "type": "boolean", 101 | "default": true, 102 | "description": "Auto sort after imports" 103 | }, 104 | "namespaceResolver.sortOnSave": { 105 | "type": "boolean", 106 | "default": false, 107 | "description": "Auto sort when a file is saved" 108 | }, 109 | "namespaceResolver.sortAlphabetically": { 110 | "type": "boolean", 111 | "default": false, 112 | "description": "Sort imports in alphabetical order instead of line length" 113 | }, 114 | "namespaceResolver.sortNatural": { 115 | "type": "boolean", 116 | "default": false, 117 | "description": "Sort imports using a 'natural order' algorithm" 118 | }, 119 | "namespaceResolver.leadingSeparator": { 120 | "type": "boolean", 121 | "default": true, 122 | "description": "Expand class with leading namespace separator" 123 | }, 124 | "namespaceResolver.highlightOnSave": { 125 | "type": "boolean", 126 | "default": false, 127 | "description": "Auto highlight not imported and not used when a file is saved" 128 | }, 129 | "namespaceResolver.highlightOnOpen": { 130 | "type": "boolean", 131 | "default": false, 132 | "description": "Auto highlight not imported and not used when a file is opened" 133 | } 134 | } 135 | }, 136 | "commands": [ 137 | { 138 | "title": "Import Class", 139 | "command": "namespaceResolver.import" 140 | }, 141 | { 142 | "title": "Import All Classes", 143 | "command": "namespaceResolver.importAll" 144 | }, 145 | { 146 | "title": "Expand Class", 147 | "command": "namespaceResolver.expand" 148 | }, 149 | { 150 | "title": "Sort Imports", 151 | "command": "namespaceResolver.sort" 152 | }, 153 | { 154 | "title": "Highlight Not Imported Classes", 155 | "command": "namespaceResolver.highlightNotImported" 156 | }, 157 | { 158 | "title": "Highlight Not Used Classes", 159 | "command": "namespaceResolver.highlightNotUsed" 160 | }, 161 | { 162 | "title": "Generate namespace for this file", 163 | "command": "namespaceResolver.generateNamespace" 164 | } 165 | ], 166 | "keybindings": [ 167 | { 168 | "command": "namespaceResolver.import", 169 | "key": "ctrl+alt+i", 170 | "when": "editorTextFocus" 171 | }, 172 | { 173 | "command": "namespaceResolver.importAll", 174 | "key": "ctrl+alt+a", 175 | "when": "editorTextFocus" 176 | }, 177 | { 178 | "command": "namespaceResolver.expand", 179 | "key": "ctrl+alt+e", 180 | "when": "editorTextFocus" 181 | }, 182 | { 183 | "command": "namespaceResolver.sort", 184 | "key": "ctrl+alt+s", 185 | "when": "editorTextFocus" 186 | }, 187 | { 188 | "command": "namespaceResolver.highlightNotImported", 189 | "key": "ctrl+alt+n", 190 | "when": "editorTextFocus" 191 | }, 192 | { 193 | "command": "namespaceResolver.highlightNotUsed", 194 | "key": "ctrl+alt+u", 195 | "when": "editorTextFocus" 196 | }, 197 | { 198 | "command": "namespaceResolver.generateNamespace", 199 | "key": "ctrl+alt+g", 200 | "when": "editorTextFocus" 201 | } 202 | ] 203 | }, 204 | "repository": { 205 | "type": "git", 206 | "url": "https://github.com/MehediDracula/PHP-Namespace-Resolver" 207 | }, 208 | "bugs": { 209 | "url": "https://github.com/MehediDracula/PHP-Namespace-Resolver/issues" 210 | }, 211 | "dependencies": { 212 | "node-natural-sort": "^0.8.6" 213 | }, 214 | "devDependencies": { 215 | "@types/vscode": "^1.68.0", 216 | "@types/node": "16.x" 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/Resolver.js: -------------------------------------------------------------------------------- 1 | let vscode = require('vscode'); 2 | let builtInClasses = require('./classes'); 3 | let naturalSort = require('node-natural-sort'); 4 | 5 | class Resolver { 6 | regexWordWithNamespace = new RegExp(/[a-zA-Z0-9\\]+/); 7 | 8 | async importCommand(selection) { 9 | let resolving = this.resolving(selection); 10 | 11 | if (resolving === undefined) { 12 | return this.showErrorMessage(`$(issue-opened) No class is selected.`); 13 | } 14 | 15 | let fqcn; 16 | let replaceClassAfterImport = false; 17 | 18 | if (/\\/.test(resolving)) { 19 | fqcn = resolving.replace(/^\\?/, ''); 20 | replaceClassAfterImport = true; 21 | } else { 22 | let files = await this.findFiles(resolving); 23 | let namespaces = await this.findNamespaces(resolving, files); 24 | 25 | fqcn = await this.pickClass(namespaces); 26 | } 27 | 28 | this.importClass(selection, fqcn, replaceClassAfterImport); 29 | } 30 | 31 | async importAll() { 32 | let text = this.activeEditor().document.getText(); 33 | let phpClasses = this.getPhpClasses(text); 34 | let useStatements = this.getUseStatementsArray(); 35 | 36 | for (let phpClass of phpClasses) { 37 | if (! useStatements.includes(phpClass)) { 38 | await this.importCommand(phpClass); 39 | } 40 | } 41 | } 42 | 43 | getPhpClasses(text) { 44 | let phpClasses = this.getExtended(text); 45 | 46 | phpClasses = phpClasses.concat(this.getFromFunctionParameters(text)); 47 | phpClasses = phpClasses.concat(this.getInitializedWithNew(text)); 48 | phpClasses = phpClasses.concat(this.getFromStaticCalls(text)); 49 | phpClasses = phpClasses.concat(this.getFromInstanceofOperator(text)); 50 | 51 | return phpClasses.filter((v, i, a) => a.indexOf(v) === i); 52 | } 53 | 54 | getExtended(text) { 55 | let regex = /extends ([A-Z][A-Za-z0-9\-\_]*)/gm; 56 | let matches = []; 57 | let phpClasses = []; 58 | 59 | while (matches = regex.exec(text)) { 60 | phpClasses.push(matches[1]); 61 | } 62 | 63 | return phpClasses; 64 | } 65 | 66 | getFromFunctionParameters(text) { 67 | let regex = /function [\S]+\((.*)\)/gm; 68 | let matches = []; 69 | let phpClasses = []; 70 | 71 | while (matches = regex.exec(text)) { 72 | let parameters = matches[1].split(', '); 73 | 74 | for (let s of parameters) { 75 | let phpClassName = s.substr(0, s.indexOf(' ')); 76 | 77 | // Starts with capital letter 78 | if (phpClassName && /[A-Z]/.test(phpClassName[0])) { 79 | phpClasses.push(phpClassName); 80 | } 81 | } 82 | } 83 | 84 | return phpClasses; 85 | } 86 | 87 | getInitializedWithNew(text) { 88 | let regex = /new ([A-Z][A-Za-z0-9\-\_]*)/gm; 89 | let matches = []; 90 | let phpClasses = []; 91 | 92 | while (matches = regex.exec(text)) { 93 | phpClasses.push(matches[1]); 94 | } 95 | 96 | return phpClasses; 97 | } 98 | 99 | getFromStaticCalls(text) { 100 | let regex = /([A-Z][A-Za-z0-9\-\_]*)::/gm; 101 | let matches = []; 102 | let phpClasses = []; 103 | 104 | while (matches = regex.exec(text)) { 105 | phpClasses.push(matches[1]); 106 | } 107 | 108 | return phpClasses; 109 | } 110 | 111 | getFromInstanceofOperator(text) { 112 | let regex = /instanceof ([A-Z_][A-Za-z0-9\_]*)/gm; 113 | let matches = []; 114 | let phpClasses = []; 115 | 116 | while (matches = regex.exec(text)) { 117 | phpClasses.push(matches[1]); 118 | } 119 | 120 | return phpClasses; 121 | } 122 | 123 | async highlightNotImported() { 124 | let text = this.activeEditor().document.getText(); 125 | let phpClasses = this.getPhpClasses(text); 126 | let importedPhpClasses = this.getImportedPhpClasses(text); 127 | 128 | // Get phpClasses not present in importedPhpClasses 129 | let notImported = phpClasses.filter(function (phpClass) { 130 | return !importedPhpClasses.includes(phpClass); 131 | }); 132 | 133 | // Highlight diff 134 | let matches = []; 135 | let decorationOptions = []; 136 | 137 | for (let i = 0; i < notImported.length; i++) { 138 | let regex = new RegExp(notImported[i], 'g'); 139 | 140 | while (matches = regex.exec(text)) { 141 | let startPos = this.activeEditor().document.positionAt(matches.index); 142 | 143 | // as js does not support regex look behinds we get results 144 | // where the object name is in the middle of a string 145 | // we should drop those 146 | let textLine = this.activeEditor().document.lineAt(startPos); 147 | let charBeforeMatch = textLine.text.charAt(startPos.character - 1); 148 | 149 | if (!/\w/.test(charBeforeMatch) && textLine.text.search(/namespace/) == -1) { 150 | let endPos = this.activeEditor().document.positionAt(matches.index + matches[0].length); 151 | 152 | decorationOptions.push({ 153 | range: new vscode.Range(startPos, endPos), 154 | hoverMessage: 'Class is not imported.', 155 | }); 156 | } 157 | } 158 | } 159 | 160 | // TODO have these in settings 161 | let decorationType = vscode.window.createTextEditorDecorationType({ 162 | backgroundColor: 'rgba(255,155,0, 0.5)', 163 | light: { 164 | borderColor: 'darkblue' 165 | }, 166 | dark: { 167 | borderColor: 'lightblue' 168 | } 169 | }); 170 | 171 | this.activeEditor().setDecorations(decorationType, decorationOptions); 172 | } 173 | 174 | async highlightNotUsed() { 175 | const text = this.activeEditor().document.getText(); 176 | const phpClasses = this.getPhpClasses(text); 177 | const importedPhpClasses = this.getImportedPhpClasses(text); 178 | 179 | // Get phpClasses not present in importedPhpClasses 180 | let notUsed = importedPhpClasses.filter(function (phpClass) { 181 | return ! phpClasses.includes(phpClass); 182 | }); 183 | 184 | // Highlight diff 185 | let matches = []; 186 | let decorationOptions = []; 187 | 188 | for (let i = 0; i < notUsed.length; i++) { 189 | let regex = new RegExp(notUsed[i], 'g'); 190 | 191 | while (matches = regex.exec(text)) { 192 | let startPos = this.activeEditor().document.positionAt(matches.index); 193 | let textLine = this.activeEditor().document.lineAt(startPos); 194 | 195 | if (textLine.text.search(/use/) != -1) { 196 | let endPos = this.activeEditor().document.positionAt(matches.index + matches[0].length); 197 | 198 | decorationOptions.push({ 199 | range: new vscode.Range(startPos, endPos), 200 | hoverMessage: 'Class is not used.', 201 | }); 202 | } 203 | } 204 | } 205 | 206 | // TODO have these in settings 207 | const decorationType = vscode.window.createTextEditorDecorationType({ 208 | backgroundColor: 'rgba(255,55,55, 0.5)', 209 | light: { 210 | borderColor: 'darkblue' 211 | }, 212 | dark: { 213 | borderColor: 'lightblue' 214 | } 215 | }); 216 | 217 | this.activeEditor().setDecorations(decorationType, decorationOptions); 218 | } 219 | 220 | getImportedPhpClasses(text) { 221 | let regex = /use (.*);/gm; 222 | let matches = []; 223 | let importedPhpClasses = []; 224 | 225 | while (matches = regex.exec(text)) { 226 | let className = matches[1].split('\\').pop(); 227 | 228 | importedPhpClasses.push(className); 229 | } 230 | 231 | return importedPhpClasses; 232 | } 233 | 234 | importClass(selection, fqcn, replaceClassAfterImport = false) { 235 | let useStatements, declarationLines; 236 | 237 | try { 238 | [useStatements, declarationLines] = this.getDeclarations(fqcn); 239 | } catch (error) { 240 | return this.showErrorMessage(error.message); 241 | } 242 | 243 | let classBaseName = fqcn.match(/(\w+)/g).pop(); 244 | 245 | if (this.hasConflict(useStatements, classBaseName)) { 246 | this.insertAsAlias(selection, fqcn, useStatements, declarationLines); 247 | } else if (replaceClassAfterImport) { 248 | this.importAndReplaceSelectedClass(selection, classBaseName, fqcn, declarationLines); 249 | } else { 250 | this.insert(fqcn, declarationLines); 251 | } 252 | } 253 | 254 | async insert(fqcn, declarationLines, alias = null) { 255 | let [prepend, append, insertLine] = this.getInsertLine(declarationLines); 256 | 257 | await this.activeEditor().edit(textEdit => { 258 | textEdit.replace( 259 | new vscode.Position((insertLine), 0), 260 | (`${prepend}use ${fqcn}`) + (alias !== null ? ` as ${alias}` : '') + (`;${append}`) 261 | ); 262 | }); 263 | 264 | if (this.config('autoSort')) { 265 | this.sortImports(); 266 | } 267 | 268 | this.showMessage('$(check) The class is imported.'); 269 | } 270 | 271 | async insertAsAlias(selection, fqcn, useStatements, declarationLines) { 272 | let alias = await vscode.window.showInputBox({ 273 | placeHolder: 'Enter an alias or leave it empty to replace' 274 | }); 275 | 276 | if (alias === undefined) { 277 | return; 278 | } 279 | 280 | if (this.hasConflict(useStatements, alias)) { 281 | this.showErrorMessage(`$(issue-opened) This alias is already in use.`); 282 | 283 | this.insertAsAlias(selection, fqcn, useStatements, declarationLines) 284 | } else if (alias !== '') { 285 | this.importAndReplaceSelectedClass(selection, alias, fqcn, declarationLines, alias); 286 | } else if (alias === '') { 287 | this.replaceUseStatement(fqcn, useStatements); 288 | } 289 | } 290 | 291 | async replaceUseStatement(fqcn, useStatements) { 292 | let useStatement = useStatements.find(use => { 293 | let className = use.text.match(/(\w+)?;/).pop(); 294 | 295 | return fqcn.endsWith(className); 296 | }); 297 | 298 | await this.activeEditor().edit(textEdit => { 299 | textEdit.replace( 300 | new vscode.Range(useStatement.line, 0, useStatement.line, useStatement.text.length), 301 | `use ${fqcn};` 302 | ); 303 | }); 304 | 305 | if (this.config('autoSort')) { 306 | this.sortImports(); 307 | } 308 | } 309 | 310 | async replaceNamespaceStatement(namespace, line) { 311 | let realLine = line - 1; 312 | let text = this.activeEditor().document.lineAt(realLine).text; 313 | let newNs = text.replace(/namespace (.+)/, namespace); 314 | 315 | await this.activeEditor().edit(textEdit => { 316 | textEdit.replace( 317 | new vscode.Range(realLine, 0, realLine, text.length), 318 | newNs.trim() 319 | ); 320 | }); 321 | } 322 | 323 | async importAndReplaceSelectedClass(selection, replacingClassName, fqcn, declarationLines, alias = null) { 324 | await this.changeSelectedClass(selection, replacingClassName, false); 325 | 326 | this.insert(fqcn, declarationLines, alias); 327 | } 328 | 329 | async expandCommand(selection) { 330 | let resolving = this.resolving(selection); 331 | 332 | if (resolving === null) { 333 | return this.showErrorMessage(`$(issue-opened) No class is selected.`); 334 | } 335 | 336 | let files = await this.findFiles(resolving); 337 | let namespaces = await this.findNamespaces(resolving, files); 338 | let fqcn = await this.pickClass(namespaces); 339 | 340 | this.changeSelectedClass(selection, fqcn, true); 341 | } 342 | 343 | async changeSelectedClass(selection, fqcn, prependBackslash = false) { 344 | await this.activeEditor().edit(textEdit => { 345 | textEdit.replace( 346 | this.activeEditor().document.getWordRangeAtPosition(selection.active, this.regexWordWithNamespace), 347 | (prependBackslash && this.config('leadingSeparator') ? '\\' : '') + fqcn 348 | ); 349 | }); 350 | 351 | let newPosition = new vscode.Position(selection.active.line, selection.active.character); 352 | 353 | this.activeEditor().selection = new vscode.Selection(newPosition, newPosition); 354 | } 355 | 356 | sortCommand() { 357 | try { 358 | this.sortImports(); 359 | } catch (error) { 360 | return this.showErrorMessage(error.message); 361 | } 362 | 363 | this.showMessage('$(check) Imports are sorted.'); 364 | } 365 | 366 | findFiles(resolving) { 367 | return vscode.workspace.findFiles(`**/${resolving}.php`, this.config('exclude')); 368 | } 369 | 370 | findNamespaces(resolving, files) { 371 | return new Promise((resolve, reject) => { 372 | let textDocuments = this.getTextDocuments(files, resolving); 373 | 374 | Promise.all(textDocuments).then(docs => { 375 | let parsedNamespaces = this.parseNamespaces(docs, resolving); 376 | 377 | if (parsedNamespaces.length === 0) { 378 | return this.showErrorMessage(`$(circle-slash) The class is not found.`); 379 | } 380 | 381 | resolve(parsedNamespaces); 382 | }); 383 | }); 384 | } 385 | 386 | pickClass(namespaces) { 387 | return new Promise((resolve, reject) => { 388 | if (namespaces.length === 1) { 389 | // Only one namespace found so no need to show picker. 390 | return resolve(namespaces[0]); 391 | } 392 | 393 | vscode.window.showQuickPick(namespaces).then(picked => { 394 | if (picked !== undefined) { 395 | resolve(picked); 396 | } 397 | }); 398 | }) 399 | } 400 | 401 | getTextDocuments(files, resolving) { 402 | let textDocuments = []; 403 | 404 | for (let i = 0; i < files.length; i++) { 405 | let fileName = files[i].fsPath.replace(/^.*[\\\/]/, '').split('.')[0]; 406 | 407 | if (fileName !== resolving) { 408 | continue; 409 | } 410 | 411 | textDocuments.push(vscode.workspace.openTextDocument(files[i])); 412 | } 413 | 414 | return textDocuments; 415 | } 416 | 417 | parseNamespaces(docs, resolving) { 418 | let parsedNamespaces = []; 419 | 420 | for (let i = 0; i < docs.length; i++) { 421 | for (let line = 0; line < docs[i].lineCount; line++) { 422 | let textLine = docs[i].lineAt(line).text; 423 | 424 | if (textLine.startsWith('namespace ') || textLine.startsWith(' 0) { 445 | parsedNamespaces.push(resolving); 446 | } 447 | 448 | return parsedNamespaces; 449 | } 450 | 451 | sortImports() { 452 | let [useStatements,] = this.getDeclarations(); 453 | 454 | if (useStatements.length <= 1) { 455 | throw new Error('$(issue-opened) Nothing to sort.'); 456 | } 457 | 458 | let sortFunction = (a, b) => { 459 | if (this.config('sortAlphabetically')) { 460 | if (a.text.toLowerCase() < b.text.toLowerCase()) return -1; 461 | if (a.text.toLowerCase() > b.text.toLowerCase()) return 1; 462 | return 0; 463 | } else { 464 | if (a.text.length == b.text.length) { 465 | if (a.text.toLowerCase() < b.text.toLowerCase()) return -1; 466 | if (a.text.toLowerCase() > b.text.toLowerCase()) return 1; 467 | } 468 | 469 | return a.text.length - b.text.length; 470 | } 471 | } 472 | 473 | if (this.config('sortNatural')) { 474 | let natsort = naturalSort({ 475 | caseSensitive: true, 476 | order: this.config('sortAlphabetically') ? 'ASC' : 'DESC' 477 | }); 478 | 479 | sortFunction = (a, b) => { 480 | return natsort(a.text, b.text); 481 | }; 482 | } 483 | 484 | let sorted = useStatements.slice().sort(sortFunction); 485 | 486 | this.activeEditor().edit(textEdit => { 487 | for (let i = 0; i < sorted.length; i++) { 488 | textEdit.replace( 489 | new vscode.Range(useStatements[i].line, 0, useStatements[i].line, useStatements[i].text.length), 490 | sorted[i].text 491 | ); 492 | } 493 | }); 494 | } 495 | 496 | activeEditor() { 497 | return vscode.window.activeTextEditor; 498 | } 499 | 500 | hasConflict(useStatements, resolving) { 501 | for (let i = 0; i < useStatements.length; i++) { 502 | if (useStatements[i].text.match(/(\w+)?;/).pop() === resolving) { 503 | return true; 504 | } 505 | } 506 | 507 | return false; 508 | } 509 | 510 | getUseStatementsArray() { 511 | let useStatements = []; 512 | 513 | for (let line = 0; line < this.activeEditor().document.lineCount; line++) { 514 | let text = this.activeEditor().document.lineAt(line).text; 515 | 516 | if (text.startsWith('use ')) { 517 | useStatements.push( 518 | text.match(/(\w+?);/)[1] 519 | ); 520 | } else if (/(class|trait|interface)\s+\w+/.test(text)) { 521 | break; 522 | } 523 | } 524 | 525 | return useStatements; 526 | } 527 | 528 | getDeclarations(pickedClass = null) { 529 | let useStatements = []; 530 | let declarationLines = { 531 | PHPTag: 0, 532 | namespace: null, 533 | useStatement: null, 534 | class: null 535 | }; 536 | 537 | for (let line = 0; line < this.activeEditor().document.lineCount; line++) { 538 | let text = this.activeEditor().document.lineAt(line).text; 539 | 540 | if (pickedClass !== null && text === `use ${pickedClass};`) { 541 | throw new Error('$(issue-opened) The class is already imported.'); 542 | } 543 | 544 | // break if all declarations were found. 545 | if (declarationLines.PHPTag && declarationLines.namespace && 546 | declarationLines.useStatement && declarationLines.class) { 547 | break; 548 | } 549 | 550 | if (text.startsWith(' { 657 | let composerJson = JSON.parse(document.getText()); 658 | let psr4 = (composerJson.autoload || {})['psr-4']; 659 | 660 | if (psr4 === undefined) { 661 | return this.showErrorMessage('No psr-4 key in composer.json autoload object, automatic namespace generation failed'); 662 | } 663 | 664 | let devPsr4 = (composerJson['autoload-dev'] || {})['psr-4']; 665 | 666 | if (devPsr4 !== undefined) { 667 | psr4 = {...psr4, ...devPsr4}; 668 | } 669 | 670 | let currentRelativePath = currentPath.split(composerPath)[1]; 671 | 672 | //this is a way to always match with psr-4 entries 673 | if(!currentRelativePath.endsWith('/')) { 674 | currentRelativePath += '/'; 675 | } 676 | 677 | let namespaceBase = Object.keys(psr4).filter(function (namespaceBase) { 678 | return currentRelativePath.lastIndexOf(psr4[namespaceBase]) !== -1; 679 | })[0]; 680 | 681 | let baseDir = psr4[namespaceBase]; 682 | 683 | namespaceBase = namespaceBase.replace(/\\$/, ''); 684 | 685 | let namespace = currentPath.substring(currentPath.lastIndexOf(baseDir)+baseDir.length); 686 | 687 | if (namespace !== "") { 688 | namespace = namespace.replace(/\//g, '\\'); 689 | namespace = namespace.replace(/^\\/, ''); 690 | namespace = namespace.replace(/\\$/, ''); 691 | namespace = namespaceBase + '\\' + namespace; 692 | } else { 693 | namespace = namespaceBase; 694 | } 695 | 696 | namespace = 'namespace ' + namespace + ';' + "\n" 697 | 698 | let declarationLines; 699 | 700 | try { 701 | [, declarationLines] = this.getDeclarations(); 702 | } catch (error) { 703 | return this.showErrorMessage(error.message); 704 | } 705 | 706 | if (declarationLines.namespace !== null) { 707 | this.replaceNamespaceStatement(namespace, declarationLines.namespace); 708 | } else { 709 | this.activeEditor().edit(textEdit => { 710 | textEdit.insert(new vscode.Position(1, 0), namespace); 711 | }); 712 | } 713 | }); 714 | } 715 | } 716 | 717 | module.exports = Resolver; -------------------------------------------------------------------------------- /src/classes.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | "stdClass", 3 | "Exception", 4 | "ErrorException", 5 | "Error", 6 | "ParseError", 7 | "TypeError", 8 | "ArgumentCountError", 9 | "ArithmeticError", 10 | "DivisionByZeroError", 11 | "Closure", 12 | "Generator", 13 | "ClosedGeneratorException", 14 | "DateTime", 15 | "DateTimeImmutable", 16 | "DateTimeZone", 17 | "DateInterval", 18 | "DatePeriod", 19 | "LibXMLError", 20 | "ReflectionException", 21 | "Reflection", 22 | "ReflectionFunctionAbstract", 23 | "ReflectionFunction", 24 | "ReflectionGenerator", 25 | "ReflectionParameter", 26 | "ReflectionType", 27 | "ReflectionNamedType", 28 | "ReflectionMethod", 29 | "ReflectionClass", 30 | "ReflectionObject", 31 | "ReflectionProperty", 32 | "ReflectionClassConstant", 33 | "ReflectionExtension", 34 | "ReflectionZendExtension", 35 | "LogicException", 36 | "BadFunctionCallException", 37 | "BadMethodCallException", 38 | "DomainException", 39 | "InvalidArgumentException", 40 | "LengthException", 41 | "OutOfRangeException", 42 | "RuntimeException", 43 | "OutOfBoundsException", 44 | "OverflowException", 45 | "RangeException", 46 | "UnderflowException", 47 | "UnexpectedValueException", 48 | "RecursiveIteratorIterator", 49 | "IteratorIterator", 50 | "FilterIterator", 51 | "RecursiveFilterIterator", 52 | "CallbackFilterIterator", 53 | "RecursiveCallbackFilterIterator", 54 | "ParentIterator", 55 | "LimitIterator", 56 | "CachingIterator", 57 | "RecursiveCachingIterator", 58 | "NoRewindIterator", 59 | "AppendIterator", 60 | "InfiniteIterator", 61 | "RegexIterator", 62 | "RecursiveRegexIterator", 63 | "EmptyIterator", 64 | "RecursiveTreeIterator", 65 | "ArrayObject", 66 | "ArrayIterator", 67 | "RecursiveArrayIterator", 68 | "SplFileInfo", 69 | "DirectoryIterator", 70 | "FilesystemIterator", 71 | "RecursiveDirectoryIterator", 72 | "GlobIterator", 73 | "SplFileObject", 74 | "SplTempFileObject", 75 | "SplDoublyLinkedList", 76 | "SplQueue", 77 | "SplStack", 78 | "SplHeap", 79 | "SplMinHeap", 80 | "SplMaxHeap", 81 | "SplPriorityQueue", 82 | "SplFixedArray", 83 | "SplObjectStorage", 84 | "MultipleIterator", 85 | "SessionHandler", 86 | "__PHP_Incomplete_Class", 87 | "php_user_filter", 88 | "Directory", 89 | "AssertionError", 90 | "PDOException", 91 | "PDO", 92 | "PDOStatement", 93 | "PDORow", 94 | "CURLFile", 95 | "DOMException", 96 | "DOMStringList", 97 | "DOMNameList", 98 | "DOMImplementationList", 99 | "DOMImplementationSource", 100 | "DOMImplementation", 101 | "DOMNode", 102 | "DOMNameSpaceNode", 103 | "DOMDocumentFragment", 104 | "DOMDocument", 105 | "DOMNodeList", 106 | "DOMNamedNodeMap", 107 | "DOMCharacterData", 108 | "DOMAttr", 109 | "DOMElement", 110 | "DOMText", 111 | "DOMComment", 112 | "DOMTypeinfo", 113 | "DOMUserDataHandler", 114 | "DOMDomError", 115 | "DOMErrorHandler", 116 | "DOMLocator", 117 | "DOMConfiguration", 118 | "DOMCdataSection", 119 | "DOMDocumentType", 120 | "DOMNotation", 121 | "DOMEntity", 122 | "DOMEntityReference", 123 | "DOMProcessingInstruction", 124 | "DOMStringExtend", 125 | "DOMXPath", 126 | "finfo", 127 | "Collator", 128 | "NumberFormatter", 129 | "Normalizer", 130 | "Locale", 131 | "MessageFormatter", 132 | "IntlDateFormatter", 133 | "ResourceBundle", 134 | "Transliterator", 135 | "IntlTimeZone", 136 | "IntlCalendar", 137 | "IntlGregorianCalendar", 138 | "Spoofchecker", 139 | "IntlException", 140 | "IntlIterator", 141 | "IntlBreakIterator", 142 | "IntlRuleBasedBreakIterator", 143 | "IntlCodePointBreakIterator", 144 | "IntlPartsIterator", 145 | "UConverter", 146 | "IntlChar", 147 | "mysqli_sql_exception", 148 | "mysqli_driver", 149 | "mysqli", 150 | "mysqli_warning", 151 | "mysqli_result", 152 | "mysqli_stmt", 153 | "PharException", 154 | "Phar", 155 | "PharData", 156 | "PharFileInfo", 157 | "SimpleXMLElement", 158 | "SimpleXMLIterator", 159 | "SQLite3", 160 | "SQLite3Stmt", 161 | "SQLite3Result", 162 | "XMLReader", 163 | "XMLWriter", 164 | "XSLTProcessor", 165 | "ZipArchive", 166 | 167 | // Interfaces 168 | "Traversable", 169 | "IteratorAggregate", 170 | "Iterator", 171 | "ArrayAccess", 172 | "Serializable", 173 | "Throwable", 174 | "DateTimeInterface", 175 | "Reflector", 176 | "RecursiveIterator", 177 | "OuterIterator", 178 | "Countable", 179 | "SeekableIterator", 180 | "SplObserver", 181 | "SplSubject", 182 | "SessionHandlerInterface", 183 | "SessionIdInterface", 184 | "SessionUpdateTimestampHandlerInterface", 185 | "JsonSerializable", 186 | ]; 187 | -------------------------------------------------------------------------------- /src/extension.js: -------------------------------------------------------------------------------- 1 | let vscode = require('vscode'); 2 | let Resolver = require('./Resolver'); 3 | 4 | function activate(context) { 5 | let resolver = new Resolver; 6 | 7 | context.subscriptions.push( 8 | vscode.commands.registerCommand('namespaceResolver.import', async () => { 9 | let selections = vscode.window.activeTextEditor.selections; 10 | 11 | for (let i = 0; i < selections.length; i++) { 12 | await resolver.importCommand(selections[i]); 13 | } 14 | }) 15 | ); 16 | 17 | context.subscriptions.push( 18 | vscode.commands.registerCommand('namespaceResolver.expand', async () => { 19 | let selections = vscode.window.activeTextEditor.selections; 20 | 21 | for (let i = 0; i < selections.length; i++) { 22 | await resolver.expandCommand(selections[i]); 23 | } 24 | }) 25 | ); 26 | 27 | context.subscriptions.push( 28 | vscode.commands.registerCommand('namespaceResolver.sort', () => resolver.sortCommand()) 29 | ); 30 | 31 | context.subscriptions.push( 32 | vscode.commands.registerCommand('namespaceResolver.importAll', () => resolver.importAll()) 33 | ); 34 | 35 | context.subscriptions.push( 36 | vscode.commands.registerCommand('namespaceResolver.highlightNotImported', () => resolver.highlightNotImported()) 37 | ); 38 | 39 | context.subscriptions.push( 40 | vscode.commands.registerCommand('namespaceResolver.highlightNotUsed', () => resolver.highlightNotUsed()) 41 | ); 42 | 43 | context.subscriptions.push( 44 | vscode.commands.registerCommand('namespaceResolver.generateNamespace', () => resolver.generateNamespace()) 45 | ); 46 | 47 | context.subscriptions.push(vscode.workspace.onWillSaveTextDocument((event) => { 48 | if ( 49 | event && 50 | event.document.languageId === 'php' && 51 | vscode.workspace.getConfiguration('namespaceResolver').get('sortOnSave') 52 | ) { 53 | resolver.sortCommand(); 54 | } 55 | 56 | if ( 57 | event && 58 | event.document.languageId === 'php' && 59 | vscode.workspace.getConfiguration('namespaceResolver').get('highlightOnSave') 60 | ) { 61 | resolver.highlightNotImported(); 62 | resolver.highlightNotUsed(); 63 | } 64 | })); 65 | 66 | context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor((event) => { 67 | if ( 68 | event && 69 | event.document.languageId === 'php' && 70 | vscode.workspace.getConfiguration('namespaceResolver').get('highlightOnOpen') 71 | ) { 72 | resolver.highlightNotImported(); 73 | resolver.highlightNotUsed(); 74 | } 75 | })); 76 | 77 | context.subscriptions.push(resolver); 78 | } 79 | 80 | exports.activate = activate; 81 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@16.x": 6 | version "16.11.42" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.42.tgz#d2a75c58e9b0902b82dc54bd4c13f8ef12bd1020" 8 | integrity sha512-iwLrPOopPy6V3E+1yHTpJea3bdsNso0b0utLOJJwaa/PLzqBt3GZl3stMcakc/gr89SfcNk2ki3z7Gvue9hYGQ== 9 | 10 | "@types/vscode@^1.68.0": 11 | version "1.68.1" 12 | resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.68.1.tgz#a9e5b33f825f715cae80593cb6d5d6d4cabbe998" 13 | integrity sha512-fXlaq13NT5yHh6yZ3c+UxXloTSk34mIvsNFYyQCeO5Po2BLFAwz7EZT4kQ43B64/aPcnAenyWy3QasrTofBOnQ== 14 | 15 | node-natural-sort@^0.8.6: 16 | version "0.8.6" 17 | resolved "https://registry.yarnpkg.com/node-natural-sort/-/node-natural-sort-0.8.6.tgz#01dc6badc4743b16033408f0d8589ab2e6c094cf" 18 | --------------------------------------------------------------------------------