diff --git a/src/AuthProvider.ts b/src/AuthProvider.ts index 854f2c2..52f326a 100644 --- a/src/AuthProvider.ts +++ b/src/AuthProvider.ts @@ -1,87 +1,87 @@ -'use strict'; - -import * as vscode from 'vscode'; -import Helpers from './helpers'; - - -export default class AuthProvider implements vscode.CompletionItemProvider { - private abilities: Array = []; - private models: Array = []; - - constructor () { - var self = this; - if (vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('disableAuth', false)) { - return; - } - self.loadAbilities(); - setInterval(function () { - self.loadAbilities(); - }, 60000); - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - if (func === null) { - return out; - } - - if (func && ((func.class && Helpers.tags.auth.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.auth.functions.some((fn:string) => func.function.includes(fn)))) { - if (func.paramIndex === 1) { - for (let i in this.models) { - let completeItem = new vscode.CompletionItem(this.models[i].replace(/\\/, '\\\\'), vscode.CompletionItemKind.Value); - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(completeItem); - } - } else { - for (let i in this.abilities) { - let completeItem = new vscode.CompletionItem(this.abilities[i], vscode.CompletionItemKind.Value); - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(completeItem); - } - } - } - return out; - } - - loadAbilities() { - try { - var self = this; - Helpers.getModels().then((models) => self.models = models); - Helpers.runLaravel(` - echo json_encode( - array_merge( - array_keys(Illuminate\\Support\\Facades\\Gate::abilities()), - array_values( - array_filter( - array_unique( - Illuminate\\Support\\Arr::flatten( - array_map( - function ($val, $key) { - return array_map( - function ($rm) { - return $rm->getName(); - }, - (new ReflectionClass($val))->getMethods() - ); - }, - Illuminate\\Support\\Facades\\Gate::policies(), - array_keys(Illuminate\\Support\\Facades\\Gate::policies()) - ) - ) - ), - function ($an) {return !in_array($an, ['allow', 'deny']);} - ) - ) - ) - ); - ` - ).then(function (result) { - var abilities = JSON.parse(result); - self.abilities = abilities; - }); - } catch (exception) { - console.error(exception); - } - } -} +'use strict'; + +import * as vscode from 'vscode'; +import Helpers from './helpers'; + + +export default class AuthProvider implements vscode.CompletionItemProvider { + private abilities: Array = []; + private models: Array = []; + + constructor () { + var self = this; + if (vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('disableAuth', false)) { + return; + } + self.loadAbilities(); + setInterval(function () { + self.loadAbilities(); + }, 60000); + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + if (func === null) { + return out; + } + + if (func && ((func.class && Helpers.tags.auth.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.auth.functions.some((fn:string) => func.function.includes(fn)))) { + if (func.paramIndex === 1) { + for (let i in this.models) { + let completeItem = new vscode.CompletionItem(this.models[i].replace(/\\/, '\\\\'), vscode.CompletionItemKind.Value); + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(completeItem); + } + } else { + for (let i in this.abilities) { + let completeItem = new vscode.CompletionItem(this.abilities[i], vscode.CompletionItemKind.Value); + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(completeItem); + } + } + } + return out; + } + + loadAbilities() { + try { + var self = this; + Helpers.getModels().then((models) => self.models = models); + Helpers.runLaravel(` + echo json_encode( + array_merge( + array_keys(Illuminate\\Support\\Facades\\Gate::abilities()), + array_values( + array_filter( + array_unique( + Illuminate\\Support\\Arr::flatten( + array_map( + function ($val, $key) { + return array_map( + function ($rm) { + return $rm->getName(); + }, + (new ReflectionClass($val))->getMethods() + ); + }, + Illuminate\\Support\\Facades\\Gate::policies(), + array_keys(Illuminate\\Support\\Facades\\Gate::policies()) + ) + ) + ), + function ($an) {return !in_array($an, ['allow', 'deny']);} + ) + ) + ) + ); + `, 'Auth Data' + ).then(function (result) { + var abilities = JSON.parse(result); + self.abilities = abilities; + }); + } catch (exception) { + console.error(exception); + } + } +} diff --git a/src/BladeProvider.ts b/src/BladeProvider.ts index 9f69c09..f637283 100644 --- a/src/BladeProvider.ts +++ b/src/BladeProvider.ts @@ -1,157 +1,158 @@ -'use strict'; - -import * as vscode from 'vscode'; -import Helpers from './helpers'; - - -export default class BladeProvider implements vscode.CompletionItemProvider { - private customDirectives: Array = []; - - constructor () { - var self = this; - self.loadCustomDirectives(); - setInterval(() => self.loadCustomDirectives(), 600000); - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - let isBlade = document.languageId == 'blade' || document.languageId == 'laravel-blade' || document.fileName.endsWith('.blade.php'); - if (vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('disableBlade', false) || isBlade === false) { - return []; - } - var out:Array = this.getDefaultDirectives(document, position); - - for (var i in this.customDirectives) { - var completeItem = new vscode.CompletionItem('@' + this.customDirectives[i].name + (this.customDirectives[i].hasParams ? '(...)' : ''), vscode.CompletionItemKind.Keyword); - completeItem.insertText = new vscode.SnippetString('@' + this.customDirectives[i].name + (this.customDirectives[i].hasParams ? '(${1})' : '')); - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(completeItem); - } - return out; - } - - loadCustomDirectives() { - try { - var self = this; - // - Helpers.runLaravel( - "$out = [];" + - "foreach (app(Illuminate\\View\\Compilers\\BladeCompiler::class)->getCustomDirectives() as $name => $customDirective) {" + - " if ($customDirective instanceof \\Closure) {" + - " $out[] = ['name' => $name, 'hasParams' => (new ReflectionFunction($customDirective))->getNumberOfParameters() >= 1];" + - " } elseif (is_array($customDirective)) {" + - " $out[] = ['name' => $name, 'hasParams' => (new ReflectionMethod($customDirective[0], $customDirective[1]))->getNumberOfParameters() >= 1];" + - " }" + - "}" + - "echo json_encode($out);" - ) - .then(function (result) { - var customDirectives = JSON.parse(result); - self.customDirectives = customDirectives; - }); - } catch (exception) { - console.error(exception); - } - } - - getDefaultDirectives(document: vscode.TextDocument, position: vscode.Position): Array { - var snippets : any = { - '@if(...)': new vscode.SnippetString('@if (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endif'), - '@error(...)': new vscode.SnippetString('@error(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@enderror'), - '@if(...) ... @else ... @endif': new vscode.SnippetString('@if (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@else\n' + Helpers.getSpacer() + '${3}\n' + '@endif'), - '@foreach(...)': new vscode.SnippetString('@foreach (${1} as ${2})\n' + Helpers.getSpacer() + '${3}\n' + '@endforeach'), - '@forelse(...)': new vscode.SnippetString('@forelse (${1} as ${2})\n' + Helpers.getSpacer() + '${3}\n' + '@empty\n' + Helpers.getSpacer() + '${4}\n' + '@endforelse'), - '@for(...)': new vscode.SnippetString('@for (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endfor'), - '@while(...)': new vscode.SnippetString('@while (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endwhile'), - '@switch(...)': new vscode.SnippetString('@switch(${1})\n' + Helpers.getSpacer() + '@case(${2})\n' + Helpers.getSpacer().repeat(2) + '${3}\n' + Helpers.getSpacer().repeat(2) + '@break\n\n' + Helpers.getSpacer() + '@default\n' + Helpers.getSpacer().repeat(2) + '${4}\n@endswitch'), - '@case(...)': new vscode.SnippetString('@case(${1})\n' + Helpers.getSpacer() + '${2}\n@break'), - '@break': new vscode.SnippetString('@break'), - '@continue': new vscode.SnippetString('@continue'), - '@break(...)': new vscode.SnippetString('@break(${1})'), - '@continue(...)': new vscode.SnippetString('@continue(${1})'), - '@default': new vscode.SnippetString('@default'), - '@extends(...)': new vscode.SnippetString('@extends(${1})'), - '@empty': new vscode.SnippetString('@empty'), - '@verbatim ...': new vscode.SnippetString('@verbatim\n' + Helpers.getSpacer() + '${2}\n' + '@endverbatim'), - '@json(...)': new vscode.SnippetString('@json(${1})'), - '@elseif (...)': new vscode.SnippetString('@elseif (${1})'), - '@else': new vscode.SnippetString('@else'), - '@unless(...)': new vscode.SnippetString('@unless (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endunless'), - '@isset(...)': new vscode.SnippetString('@isset(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endisset'), - '@empty(...)': new vscode.SnippetString('@empty(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endempty'), - '@auth': new vscode.SnippetString('@auth\n' + Helpers.getSpacer() + '${1}\n' + '@endauth'), - '@guest': new vscode.SnippetString('@guest\n' + Helpers.getSpacer() + '${1}\n' + '@endguest'), - '@auth(...)': new vscode.SnippetString('@auth(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endauth'), - '@guest(...)': new vscode.SnippetString('@guest(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endguest'), - '@can(...)': new vscode.SnippetString('@can(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endcan'), - '@cannot(...)': new vscode.SnippetString('@cannot(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endcannot'), - '@elsecan(...)': new vscode.SnippetString('@elsecan(${1})'), - '@elsecannot(...)': new vscode.SnippetString('@elsecannot(${1})'), - '@production': new vscode.SnippetString('@production\n' + Helpers.getSpacer() + '${1}\n' + '@endproduction'), - '@env(...)': new vscode.SnippetString('@env(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endenv'), - '@hasSection(...)': new vscode.SnippetString('@hasSection(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endif'), - '@sectionMissing(...)': new vscode.SnippetString('@sectionMissing(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endif'), - '@include(...)': new vscode.SnippetString('@include(${1})'), - '@includeIf(...)': new vscode.SnippetString('@includeIf(${1})'), - '@includeWhen(...)': new vscode.SnippetString('@includeWhen(${1}, ${2})'), - '@includeUnless(...)': new vscode.SnippetString('@includeUnless(${1}, ${2})'), - '@includeFirst(...)': new vscode.SnippetString('@includeFirst(${1})'), - '@each(...)': new vscode.SnippetString('@each(${1}, ${2}, ${3})'), - '@once': new vscode.SnippetString('@production\n' + Helpers.getSpacer() + '${1}\n' + '@endonce'), - '@yield(...)': new vscode.SnippetString('@yield(${1})'), - '@slot(...)': new vscode.SnippetString('@slot(${1})'), - '@stack(...)': new vscode.SnippetString('@stack(${1})'), - '@push(...)': new vscode.SnippetString('@push(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endpush'), - '@prepend(...)': new vscode.SnippetString('@prepend(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endprepend'), - '@php': new vscode.SnippetString('@php\n' + Helpers.getSpacer() + '${1}\n' + '@endphp'), - '@component(...)': new vscode.SnippetString('@component(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endcomponent'), - '@section(...) ... @endsection': new vscode.SnippetString('@section(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endsection'), - '@section(...)': new vscode.SnippetString('@section(${1})'), - '@props(...)': new vscode.SnippetString('@props(${1})'), - '@show': new vscode.SnippetString('@show'), - '@stop': new vscode.SnippetString('@stop'), - '@parent': new vscode.SnippetString('@parent'), - '@csrf': new vscode.SnippetString('@csrf'), - '@method(...)': new vscode.SnippetString('@method(${1})'), - '@inject(...)': new vscode.SnippetString('@inject(${1}, ${2})'), - '@dump(...)': new vscode.SnippetString('@dump(${1})'), - '@dd(...)': new vscode.SnippetString('@dd(${1})'), - '@lang(...)': new vscode.SnippetString('@lang(${1})'), - - '@endif': new vscode.SnippetString('@endif'), - '@enderror': new vscode.SnippetString('@enderror'), - '@endforeach': new vscode.SnippetString('@endforeach'), - '@endforelse': new vscode.SnippetString('@endforelse'), - '@endfor': new vscode.SnippetString('@endfor'), - '@endwhile': new vscode.SnippetString('@endwhile'), - '@endswitch': new vscode.SnippetString('@endswitch'), - '@endverbatim': new vscode.SnippetString('@endverbatim'), - '@endunless': new vscode.SnippetString('@endunless'), - '@endisset': new vscode.SnippetString('@endisset'), - '@endempty': new vscode.SnippetString('@endempty'), - '@endauth': new vscode.SnippetString('@endauth'), - '@endguest': new vscode.SnippetString('@endguest'), - '@endproduction': new vscode.SnippetString('@endproduction'), - '@endenv': new vscode.SnippetString('@endenv'), - '@endonce': new vscode.SnippetString('@endonce'), - '@endpush': new vscode.SnippetString('@endpush'), - '@endprepend': new vscode.SnippetString('@endprepend'), - '@endphp': new vscode.SnippetString('@endphp'), - '@endcomponent': new vscode.SnippetString('@endcomponent'), - '@endsection': new vscode.SnippetString('@endsection'), - '@endslot': new vscode.SnippetString('@endslot'), - '@endcan': new vscode.SnippetString('@endcan'), - '@endcannot': new vscode.SnippetString('@endcannot'), - }; - - var out:Array = []; - for (let snippet in snippets) { - var completeItem = new vscode.CompletionItem(snippet, vscode.CompletionItemKind.Keyword); - if (snippets[snippet] instanceof vscode.SnippetString) { - completeItem.insertText = snippets[snippet]; - } - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(completeItem); - } - return out; - } -} +'use strict'; + +import * as vscode from 'vscode'; +import Helpers from './helpers'; + + +export default class BladeProvider implements vscode.CompletionItemProvider { + private customDirectives: Array = []; + + constructor () { + var self = this; + self.loadCustomDirectives(); + setInterval(() => self.loadCustomDirectives(), 600000); + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + let isBlade = document.languageId == 'blade' || document.languageId == 'laravel-blade' || document.fileName.endsWith('.blade.php'); + if (vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('disableBlade', false) || isBlade === false) { + return []; + } + var out:Array = this.getDefaultDirectives(document, position); + + for (var i in this.customDirectives) { + var completeItem = new vscode.CompletionItem('@' + this.customDirectives[i].name + (this.customDirectives[i].hasParams ? '(...)' : ''), vscode.CompletionItemKind.Keyword); + completeItem.insertText = new vscode.SnippetString('@' + this.customDirectives[i].name + (this.customDirectives[i].hasParams ? '(${1})' : '')); + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(completeItem); + } + return out; + } + + loadCustomDirectives() { + try { + var self = this; + // + Helpers.runLaravel( + "$out = [];" + + "foreach (app(Illuminate\\View\\Compilers\\BladeCompiler::class)->getCustomDirectives() as $name => $customDirective) {" + + " if ($customDirective instanceof \\Closure) {" + + " $out[] = ['name' => $name, 'hasParams' => (new ReflectionFunction($customDirective))->getNumberOfParameters() >= 1];" + + " } elseif (is_array($customDirective)) {" + + " $out[] = ['name' => $name, 'hasParams' => (new ReflectionMethod($customDirective[0], $customDirective[1]))->getNumberOfParameters() >= 1];" + + " }" + + "}" + + "echo json_encode($out);", + "Custom Blade Directives" + ) + .then(function (result) { + var customDirectives = JSON.parse(result); + self.customDirectives = customDirectives; + }); + } catch (exception) { + console.error(exception); + } + } + + getDefaultDirectives(document: vscode.TextDocument, position: vscode.Position): Array { + var snippets : any = { + '@if(...)': new vscode.SnippetString('@if (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endif'), + '@error(...)': new vscode.SnippetString('@error(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@enderror'), + '@if(...) ... @else ... @endif': new vscode.SnippetString('@if (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@else\n' + Helpers.getSpacer() + '${3}\n' + '@endif'), + '@foreach(...)': new vscode.SnippetString('@foreach (${1} as ${2})\n' + Helpers.getSpacer() + '${3}\n' + '@endforeach'), + '@forelse(...)': new vscode.SnippetString('@forelse (${1} as ${2})\n' + Helpers.getSpacer() + '${3}\n' + '@empty\n' + Helpers.getSpacer() + '${4}\n' + '@endforelse'), + '@for(...)': new vscode.SnippetString('@for (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endfor'), + '@while(...)': new vscode.SnippetString('@while (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endwhile'), + '@switch(...)': new vscode.SnippetString('@switch(${1})\n' + Helpers.getSpacer() + '@case(${2})\n' + Helpers.getSpacer().repeat(2) + '${3}\n' + Helpers.getSpacer().repeat(2) + '@break\n\n' + Helpers.getSpacer() + '@default\n' + Helpers.getSpacer().repeat(2) + '${4}\n@endswitch'), + '@case(...)': new vscode.SnippetString('@case(${1})\n' + Helpers.getSpacer() + '${2}\n@break'), + '@break': new vscode.SnippetString('@break'), + '@continue': new vscode.SnippetString('@continue'), + '@break(...)': new vscode.SnippetString('@break(${1})'), + '@continue(...)': new vscode.SnippetString('@continue(${1})'), + '@default': new vscode.SnippetString('@default'), + '@extends(...)': new vscode.SnippetString('@extends(${1})'), + '@empty': new vscode.SnippetString('@empty'), + '@verbatim ...': new vscode.SnippetString('@verbatim\n' + Helpers.getSpacer() + '${2}\n' + '@endverbatim'), + '@json(...)': new vscode.SnippetString('@json(${1})'), + '@elseif (...)': new vscode.SnippetString('@elseif (${1})'), + '@else': new vscode.SnippetString('@else'), + '@unless(...)': new vscode.SnippetString('@unless (${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endunless'), + '@isset(...)': new vscode.SnippetString('@isset(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endisset'), + '@empty(...)': new vscode.SnippetString('@empty(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endempty'), + '@auth': new vscode.SnippetString('@auth\n' + Helpers.getSpacer() + '${1}\n' + '@endauth'), + '@guest': new vscode.SnippetString('@guest\n' + Helpers.getSpacer() + '${1}\n' + '@endguest'), + '@auth(...)': new vscode.SnippetString('@auth(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endauth'), + '@guest(...)': new vscode.SnippetString('@guest(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endguest'), + '@can(...)': new vscode.SnippetString('@can(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endcan'), + '@cannot(...)': new vscode.SnippetString('@cannot(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endcannot'), + '@elsecan(...)': new vscode.SnippetString('@elsecan(${1})'), + '@elsecannot(...)': new vscode.SnippetString('@elsecannot(${1})'), + '@production': new vscode.SnippetString('@production\n' + Helpers.getSpacer() + '${1}\n' + '@endproduction'), + '@env(...)': new vscode.SnippetString('@env(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endenv'), + '@hasSection(...)': new vscode.SnippetString('@hasSection(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endif'), + '@sectionMissing(...)': new vscode.SnippetString('@sectionMissing(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endif'), + '@include(...)': new vscode.SnippetString('@include(${1})'), + '@includeIf(...)': new vscode.SnippetString('@includeIf(${1})'), + '@includeWhen(...)': new vscode.SnippetString('@includeWhen(${1}, ${2})'), + '@includeUnless(...)': new vscode.SnippetString('@includeUnless(${1}, ${2})'), + '@includeFirst(...)': new vscode.SnippetString('@includeFirst(${1})'), + '@each(...)': new vscode.SnippetString('@each(${1}, ${2}, ${3})'), + '@once': new vscode.SnippetString('@production\n' + Helpers.getSpacer() + '${1}\n' + '@endonce'), + '@yield(...)': new vscode.SnippetString('@yield(${1})'), + '@slot(...)': new vscode.SnippetString('@slot(${1})'), + '@stack(...)': new vscode.SnippetString('@stack(${1})'), + '@push(...)': new vscode.SnippetString('@push(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endpush'), + '@prepend(...)': new vscode.SnippetString('@prepend(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endprepend'), + '@php': new vscode.SnippetString('@php\n' + Helpers.getSpacer() + '${1}\n' + '@endphp'), + '@component(...)': new vscode.SnippetString('@component(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endcomponent'), + '@section(...) ... @endsection': new vscode.SnippetString('@section(${1})\n' + Helpers.getSpacer() + '${2}\n' + '@endsection'), + '@section(...)': new vscode.SnippetString('@section(${1})'), + '@props(...)': new vscode.SnippetString('@props(${1})'), + '@show': new vscode.SnippetString('@show'), + '@stop': new vscode.SnippetString('@stop'), + '@parent': new vscode.SnippetString('@parent'), + '@csrf': new vscode.SnippetString('@csrf'), + '@method(...)': new vscode.SnippetString('@method(${1})'), + '@inject(...)': new vscode.SnippetString('@inject(${1}, ${2})'), + '@dump(...)': new vscode.SnippetString('@dump(${1})'), + '@dd(...)': new vscode.SnippetString('@dd(${1})'), + '@lang(...)': new vscode.SnippetString('@lang(${1})'), + + '@endif': new vscode.SnippetString('@endif'), + '@enderror': new vscode.SnippetString('@enderror'), + '@endforeach': new vscode.SnippetString('@endforeach'), + '@endforelse': new vscode.SnippetString('@endforelse'), + '@endfor': new vscode.SnippetString('@endfor'), + '@endwhile': new vscode.SnippetString('@endwhile'), + '@endswitch': new vscode.SnippetString('@endswitch'), + '@endverbatim': new vscode.SnippetString('@endverbatim'), + '@endunless': new vscode.SnippetString('@endunless'), + '@endisset': new vscode.SnippetString('@endisset'), + '@endempty': new vscode.SnippetString('@endempty'), + '@endauth': new vscode.SnippetString('@endauth'), + '@endguest': new vscode.SnippetString('@endguest'), + '@endproduction': new vscode.SnippetString('@endproduction'), + '@endenv': new vscode.SnippetString('@endenv'), + '@endonce': new vscode.SnippetString('@endonce'), + '@endpush': new vscode.SnippetString('@endpush'), + '@endprepend': new vscode.SnippetString('@endprepend'), + '@endphp': new vscode.SnippetString('@endphp'), + '@endcomponent': new vscode.SnippetString('@endcomponent'), + '@endsection': new vscode.SnippetString('@endsection'), + '@endslot': new vscode.SnippetString('@endslot'), + '@endcan': new vscode.SnippetString('@endcan'), + '@endcannot': new vscode.SnippetString('@endcannot'), + }; + + var out:Array = []; + for (let snippet in snippets) { + var completeItem = new vscode.CompletionItem(snippet, vscode.CompletionItemKind.Keyword); + if (snippets[snippet] instanceof vscode.SnippetString) { + completeItem.insertText = snippets[snippet]; + } + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(completeItem); + } + return out; + } +} diff --git a/src/ConfigProvider.ts b/src/ConfigProvider.ts index f5a0a94..70a46e9 100644 --- a/src/ConfigProvider.ts +++ b/src/ConfigProvider.ts @@ -1,84 +1,84 @@ -'use strict'; - -import * as vscode from 'vscode'; -import Helpers from './helpers'; - - -export default class ConfigProvider implements vscode.CompletionItemProvider { - private timer: any = null; - private configs: Array = []; - private watcher: any = null; - - constructor () { - var self = this; - self.loadConfigs(); - if (vscode.workspace.workspaceFolders !== undefined) { - this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "config/{,*,**/*}.php")); - this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); - this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); - this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); - } - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - if (func === null) { - return out; - } - - if (func && ((func.class && Helpers.tags.config.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.config.functions.some((fn:string) => func.function.includes(fn)))) { - for (var i in this.configs) { - var completeItem = new vscode.CompletionItem(this.configs[i].name, vscode.CompletionItemKind.Value); - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - if (this.configs[i].value) { - completeItem.detail = this.configs[i].value.toString(); - } - out.push(completeItem); - } - } - return out; - } - - onChange() { - var self = this; - if (self.timer !== null) { - clearTimeout(self.timer); - } - self.timer = setTimeout(function () { - self.loadConfigs(); - self.timer = null; - }, 5000); - } - - loadConfigs() { - try { - var self = this; - Helpers.runLaravel("echo json_encode(config()->all());") - .then(function (result) { - var configs = JSON.parse(result); - self.configs = self.getConfigs(configs); - }); - } catch (exception) { - console.error(exception); - } - } - - getConfigs(conf: any): Array { - var out: Array = []; - for (var i in conf) { - if (conf[i] instanceof Array) { - out.push({name: i, value: 'array(...)'}); - } else if (conf[i] instanceof Object) { - out.push({name: i, value: 'array(...)'}); - out = out.concat(this.getConfigs(conf[i]).map(function (c) { - c.name = i + "." + c.name; - return c; - })); - } else { - out.push({name: i, value: conf[i]}); - } - } - return out; - } -} +'use strict'; + +import * as vscode from 'vscode'; +import Helpers from './helpers'; + + +export default class ConfigProvider implements vscode.CompletionItemProvider { + private timer: any = null; + private configs: Array = []; + private watcher: any = null; + + constructor () { + var self = this; + self.loadConfigs(); + if (vscode.workspace.workspaceFolders !== undefined) { + this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "config/{,*,**/*}.php")); + this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); + this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); + this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); + } + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + if (func === null) { + return out; + } + + if (func && ((func.class && Helpers.tags.config.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.config.functions.some((fn:string) => func.function.includes(fn)))) { + for (var i in this.configs) { + var completeItem = new vscode.CompletionItem(this.configs[i].name, vscode.CompletionItemKind.Value); + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + if (this.configs[i].value) { + completeItem.detail = this.configs[i].value.toString(); + } + out.push(completeItem); + } + } + return out; + } + + onChange() { + var self = this; + if (self.timer !== null) { + clearTimeout(self.timer); + } + self.timer = setTimeout(function () { + self.loadConfigs(); + self.timer = null; + }, 5000); + } + + loadConfigs() { + try { + var self = this; + Helpers.runLaravel("echo json_encode(config()->all());", "Configs") + .then(function (result) { + var configs = JSON.parse(result); + self.configs = self.getConfigs(configs); + }); + } catch (exception) { + console.error(exception); + } + } + + getConfigs(conf: any): Array { + var out: Array = []; + for (var i in conf) { + if (conf[i] instanceof Array) { + out.push({name: i, value: 'array(...)'}); + } else if (conf[i] instanceof Object) { + out.push({name: i, value: 'array(...)'}); + out = out.concat(this.getConfigs(conf[i]).map(function (c) { + c.name = i + "." + c.name; + return c; + })); + } else { + out.push({name: i, value: conf[i]}); + } + } + return out; + } +} diff --git a/src/EloquentProvider.ts b/src/EloquentProvider.ts index 68d2260..f6282cd 100644 --- a/src/EloquentProvider.ts +++ b/src/EloquentProvider.ts @@ -1,241 +1,242 @@ -'use strict'; - -import * as vscode from 'vscode'; -import Helpers from './helpers'; - - -export default class EloquentProvider implements vscode.CompletionItemProvider { - private timer: any = null; - private models: {[key:string]: any} = {}; - private watchers: Array = []; - - constructor () { - var self = this; - if (vscode.workspace.workspaceFolders !== undefined) { - for (let modelsPath of vscode.workspace.getConfiguration("LaravelExtraIntellisense").get>('modelsPaths', ['app', 'app/Models']).concat(['database/migrations'])) { - let watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], modelsPath + '/*.php')); - watcher.onDidChange((e: vscode.Uri) => self.onChange()); - watcher.onDidCreate((e: vscode.Uri) => self.onChange()); - watcher.onDidDelete((e: vscode.Uri) => self.onChange()); - this.watchers.push(watcher); - } - } - self.loadModels(); - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - let sourceCode = document.getText(); - let sourceBeforeCursor = sourceCode.substr(0, document.offsetAt(position)); - var isFactory = sourceBeforeCursor.includes("extends Factory") || sourceBeforeCursor.includes("$factory->define("); - var match = null; - var objectName = ""; - var modelName = ""; - var modelClass = ""; - if (func != null || isFactory) { - if (func) { - let modelNameRegex = /([A-z0-9_\\]+)::[^:;]+$/g; - var namespaceRegex = /namespace\s+(.+);/g; - var namespace = ""; - while ((match = modelNameRegex.exec(sourceBeforeCursor)) !== null) { - modelName = match[1]; - } - if (modelName.length === 0) { - let variableNameRegex = /(\$([A-z0-9_\\]+))->[^;]+$/g; - while ((match = variableNameRegex.exec(sourceBeforeCursor)) !== null) { - objectName = match[2]; - } - if (objectName.length > 0) { - modelNameRegex = new RegExp("\\$" + objectName + "\\s*=\\s*([A-z0-9_\\\\]+)::[^:]", "g"); - while ((match = modelNameRegex.exec(sourceBeforeCursor)) !== null) { - modelName = match[1]; - } - } - } - if ((match = namespaceRegex.exec(sourceBeforeCursor)) !== null) { - namespace = match[1]; - } - modelClass = this.getModelClass(modelName, sourceBeforeCursor); - } else { - var factoryModelClassRegex = /(protected \$model = ([A-Za-z0-9_\\]+)::class;)|(\$factory->define\(\s*([A-Za-z0-9_\\]+)::class)/g - if ((match = factoryModelClassRegex.exec(sourceBeforeCursor)) !== null) { - if (typeof match[4] !== 'undefined') { // Laravel 7 < - modelName = match[4]; - } else { // Laravel >= 8 - modelName = match[2]; - } - } - modelClass = this.getModelClass(modelName, sourceBeforeCursor); - } - - if (typeof this.models[modelClass] !== 'undefined') { - if (func && Helpers.relationMethods.some((fn:string) => func.function.includes(fn))) { - out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].relations)); - } else { - out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].attributes)); - } - } - } else { - let isArrayObject = false; - let objectRegex = /(\$?([A-z0-9_\[\]]+)|(Auth::user\(\)))\-\>[A-z0-9_]*$/g; - while ((match = objectRegex.exec(sourceBeforeCursor)) !== null) { - objectName = typeof match[2] !== 'undefined' ? match[2] : match[3]; - } - if (objectName.match(/\$?[A-z0-9_]+\[.+\].*$/g)) { - isArrayObject = true; - objectName = objectName.replace(/\[.+\].*$/g, ''); - } - if (objectName.length > 0 && objectName != 'Auth::user()') { - let modelNameRegex = new RegExp("\\$" + objectName + "\\s*=\\s*([A-z0-9_\\\\]+)::[^:;]", "g"); - while ((match = modelNameRegex.exec(sourceBeforeCursor)) !== null) { - modelName = match[1]; - } - modelClass = this.getModelClass(modelName, sourceBeforeCursor); - } - if (modelClass == 'Auth' || objectName == 'Auth::user()') { - if (typeof this.models['App\\User'] !== 'undefined') { - out = out.concat(this.getModelAttributesCompletionItems(document, position, 'App\\User')); - } else if (typeof this.models['App\\Models\\User'] !== 'undefined') { - out = out.concat(this.getModelAttributesCompletionItems(document, position, 'App\\Models\\User')); - } - } - let customVariables = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('modelVariables', {}); - for (let customVariable in customVariables) { - if (customVariable === objectName && typeof this.models[customVariables[customVariable]] !== 'undefined') { - out = out.concat(this.getModelAttributesCompletionItems(document, position, customVariables[customVariable])); - } - } - for (let i in this.models) { - if (i == modelClass || - (this.models[i].camelCase == objectName || this.models[i].snakeCase == objectName) || - (isArrayObject == true && (this.models[i].pluralCamelCase == objectName || this.models[i].pluralSnakeCase == objectName)) - ) { - out = out.concat(this.getModelAttributesCompletionItems(document, position, i)); - } - } - } - out = out.filter((v, i, a) => a.map((ai) => ai.label).indexOf(v.label) === i); // Remove duplicate items - return out; - } - - getModelClass(modelName: string, sourceBeforeCursor: string) { - let match = null; - let modelClass = ""; - if (modelName.length === 0) { - return ""; - } - var modelClassRegex = new RegExp("use (.+)" + modelName + ";", "g"); - if (modelName.substr(0, 1) === '\\') { - modelClass = modelName.substr(1); - } else if ((match = modelClassRegex.exec(sourceBeforeCursor)) !== null) { - modelClass = match[1] + modelName; - } else { - modelClass = modelName; - } - return modelClass; - } - - getModelAttributesCompletionItems(document: vscode.TextDocument, position: vscode.Position, modelClass: string) : Array { - let out: Array = []; - if (typeof this.models[modelClass] !== 'undefined') { - out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].attributes.map((attr: any) => attr[vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('modelAttributeCase', 'default')]))); - out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].accessors.map((attr: any) => attr[vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('modelAccessorCase', 'snake')]), vscode.CompletionItemKind.Constant)); - out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].relations, vscode.CompletionItemKind.Value)); - } - return out; - } - - getCompletionItems(document: vscode.TextDocument, position: vscode.Position, items: Array, type: vscode.CompletionItemKind = vscode.CompletionItemKind.Property) : Array { - let out: Array = []; - for (let item of items) { - var completeItem = new vscode.CompletionItem(item, type); - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(completeItem); - } - return out; - } - - onChange() { - var self = this; - if (self.timer !== null) { - clearTimeout(self.timer); - } - self.timer = setTimeout(function () { - self.loadModels(); - self.timer = null; - }, 5000); - } - - loadModels() { - var self = this; - try { - Helpers.runLaravel( - "foreach (['" + vscode.workspace.getConfiguration("LaravelExtraIntellisense").get>('modelsPaths', ['app', 'app/Models']).join('\', \'') + "'] as $modelPath) {" + - " if (is_dir(base_path($modelPath))) {" + - " foreach (scandir(base_path($modelPath)) as $sourceFile) {" + - " if (substr($sourceFile, -4) == '.php' && is_file(base_path(\"$modelPath/$sourceFile\"))) {" + - " include_once base_path(\"$modelPath/$sourceFile\");" + - " }" + - " }" + - " }" + - "}" + - "$modelClasses = array_values(array_filter(get_declared_classes(), function ($declaredClass) {" + - " return is_subclass_of($declaredClass, 'Illuminate\\Database\\Eloquent\\Model') && $declaredClass != 'Illuminate\\Database\\Eloquent\\Relations\\Pivot' && $declaredClass != 'Illuminate\\Foundation\\Auth\\User';" + - "}));" + - "$output = [];" + - "foreach ($modelClasses as $modelClass) {" + - " $classReflection = new \\ReflectionClass($modelClass);" + - " $output[$modelClass] = [" + - " 'name' => $classReflection->getShortName()," + - " 'camelCase' => Illuminate\\Support\\Str::camel($classReflection->getShortName())," + - " 'snakeCase' => Illuminate\\Support\\Str::snake($classReflection->getShortName())," + - " 'pluralCamelCase' => Illuminate\\Support\\Str::camel(Illuminate\\Support\\Str::plural($classReflection->getShortName()))," + - " 'pluralSnakeCase' => Illuminate\\Support\\Str::snake(Illuminate\\Support\\Str::plural($classReflection->getShortName()))," + - " 'attributes' => []," + - " 'accessors' => []," + - " 'relations' => []" + - " ];" + - " try {" + - " $modelInstance = $modelClass::first();" + - " $attributes = array_values(array_unique(array_merge(app($modelClass)->getFillable(), array_keys($modelInstance ? $modelInstance->getAttributes() : []))));" + - " $output[$modelClass]['attributes'] = array_map(function ($attribute) {" + - " return ['default' => $attribute, 'snake' => Illuminate\\Support\\Str::snake($attribute), 'camel' => Illuminate\\Support\\Str::camel($attribute)];" + - " }, $attributes);" + - " } catch (\\Throwable $e) {}" + - " foreach ($classReflection->getMethods() as $classMethod) {" + - " try {" + - " if (" + - " $classMethod->isStatic() == false &&" + - " $classMethod->isPublic() == true &&" + - " substr($classMethod->getName(), 0, 3) != 'get' &&" + - " substr($classMethod->getName(), 0, 3) != 'set' &&" + - " count($classMethod->getParameters()) == 0 &&" + - " preg_match('/belongsTo|hasMany|hasOne|morphOne|morphMany|morphTo/', implode('', array_slice(file($classMethod->getFileName()), $classMethod->getStartLine(), $classMethod->getEndLine() - $classMethod->getStartLine() - 1)))" + - " ) {" + - " $output[$modelClass]['relations'][] = $classMethod->getName();" + - " } elseif (" + - " substr($classMethod->getName(), 0, 3) == 'get' && " + - " substr($classMethod->getName(), -9) == 'Attribute' &&" + - " !empty(substr($classMethod->getName(), 3, -9))" + - " ) {" + - " $attributeName = substr($classMethod->getName(), 3, -9);" + - " $output[$modelClass]['accessors'][] = ['default' => $attributeName, 'snake' => Illuminate\\Support\\Str::snake($attributeName), 'camel' => Illuminate\\Support\\Str::camel($attributeName)];" + - " }" + - " } catch (\\Throwable $e) {}" + - " }" + - " sort($output[$modelClass]['attributes']);" + - " sort($output[$modelClass]['relations']);" + - "}" + - "echo json_encode($output);" - ).then(function (result) { - let models = JSON.parse(result); - self.models = models; - }).catch(function (e) { - console.error(e); - }); - } catch (exception) { - console.error(exception); - } - } -} +'use strict'; + +import * as vscode from 'vscode'; +import Helpers from './helpers'; + + +export default class EloquentProvider implements vscode.CompletionItemProvider { + private timer: any = null; + private models: {[key:string]: any} = {}; + private watchers: Array = []; + + constructor () { + var self = this; + if (vscode.workspace.workspaceFolders !== undefined) { + for (let modelsPath of vscode.workspace.getConfiguration("LaravelExtraIntellisense").get>('modelsPaths', ['app', 'app/Models']).concat(['database/migrations'])) { + let watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], modelsPath + '/*.php')); + watcher.onDidChange((e: vscode.Uri) => self.onChange()); + watcher.onDidCreate((e: vscode.Uri) => self.onChange()); + watcher.onDidDelete((e: vscode.Uri) => self.onChange()); + this.watchers.push(watcher); + } + } + self.loadModels(); + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + let sourceCode = document.getText(); + let sourceBeforeCursor = sourceCode.substr(0, document.offsetAt(position)); + var isFactory = sourceBeforeCursor.includes("extends Factory") || sourceBeforeCursor.includes("$factory->define("); + var match = null; + var objectName = ""; + var modelName = ""; + var modelClass = ""; + if (func != null || isFactory) { + if (func) { + let modelNameRegex = /([A-z0-9_\\]+)::[^:;]+$/g; + var namespaceRegex = /namespace\s+(.+);/g; + var namespace = ""; + while ((match = modelNameRegex.exec(sourceBeforeCursor)) !== null) { + modelName = match[1]; + } + if (modelName.length === 0) { + let variableNameRegex = /(\$([A-z0-9_\\]+))->[^;]+$/g; + while ((match = variableNameRegex.exec(sourceBeforeCursor)) !== null) { + objectName = match[2]; + } + if (objectName.length > 0) { + modelNameRegex = new RegExp("\\$" + objectName + "\\s*=\\s*([A-z0-9_\\\\]+)::[^:]", "g"); + while ((match = modelNameRegex.exec(sourceBeforeCursor)) !== null) { + modelName = match[1]; + } + } + } + if ((match = namespaceRegex.exec(sourceBeforeCursor)) !== null) { + namespace = match[1]; + } + modelClass = this.getModelClass(modelName, sourceBeforeCursor); + } else { + var factoryModelClassRegex = /(protected \$model = ([A-Za-z0-9_\\]+)::class;)|(\$factory->define\(\s*([A-Za-z0-9_\\]+)::class)/g + if ((match = factoryModelClassRegex.exec(sourceBeforeCursor)) !== null) { + if (typeof match[4] !== 'undefined') { // Laravel 7 < + modelName = match[4]; + } else { // Laravel >= 8 + modelName = match[2]; + } + } + modelClass = this.getModelClass(modelName, sourceBeforeCursor); + } + + if (typeof this.models[modelClass] !== 'undefined') { + if (func && Helpers.relationMethods.some((fn:string) => func.function.includes(fn))) { + out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].relations)); + } else { + out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].attributes)); + } + } + } else { + let isArrayObject = false; + let objectRegex = /(\$?([A-z0-9_\[\]]+)|(Auth::user\(\)))\-\>[A-z0-9_]*$/g; + while ((match = objectRegex.exec(sourceBeforeCursor)) !== null) { + objectName = typeof match[2] !== 'undefined' ? match[2] : match[3]; + } + if (objectName.match(/\$?[A-z0-9_]+\[.+\].*$/g)) { + isArrayObject = true; + objectName = objectName.replace(/\[.+\].*$/g, ''); + } + if (objectName.length > 0 && objectName != 'Auth::user()') { + let modelNameRegex = new RegExp("\\$" + objectName + "\\s*=\\s*([A-z0-9_\\\\]+)::[^:;]", "g"); + while ((match = modelNameRegex.exec(sourceBeforeCursor)) !== null) { + modelName = match[1]; + } + modelClass = this.getModelClass(modelName, sourceBeforeCursor); + } + if (modelClass == 'Auth' || objectName == 'Auth::user()') { + if (typeof this.models['App\\User'] !== 'undefined') { + out = out.concat(this.getModelAttributesCompletionItems(document, position, 'App\\User')); + } else if (typeof this.models['App\\Models\\User'] !== 'undefined') { + out = out.concat(this.getModelAttributesCompletionItems(document, position, 'App\\Models\\User')); + } + } + let customVariables = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('modelVariables', {}); + for (let customVariable in customVariables) { + if (customVariable === objectName && typeof this.models[customVariables[customVariable]] !== 'undefined') { + out = out.concat(this.getModelAttributesCompletionItems(document, position, customVariables[customVariable])); + } + } + for (let i in this.models) { + if (i == modelClass || + (this.models[i].camelCase == objectName || this.models[i].snakeCase == objectName) || + (isArrayObject == true && (this.models[i].pluralCamelCase == objectName || this.models[i].pluralSnakeCase == objectName)) + ) { + out = out.concat(this.getModelAttributesCompletionItems(document, position, i)); + } + } + } + out = out.filter((v, i, a) => a.map((ai) => ai.label).indexOf(v.label) === i); // Remove duplicate items + return out; + } + + getModelClass(modelName: string, sourceBeforeCursor: string) { + let match = null; + let modelClass = ""; + if (modelName.length === 0) { + return ""; + } + var modelClassRegex = new RegExp("use (.+)" + modelName + ";", "g"); + if (modelName.substr(0, 1) === '\\') { + modelClass = modelName.substr(1); + } else if ((match = modelClassRegex.exec(sourceBeforeCursor)) !== null) { + modelClass = match[1] + modelName; + } else { + modelClass = modelName; + } + return modelClass; + } + + getModelAttributesCompletionItems(document: vscode.TextDocument, position: vscode.Position, modelClass: string) : Array { + let out: Array = []; + if (typeof this.models[modelClass] !== 'undefined') { + out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].attributes.map((attr: any) => attr[vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('modelAttributeCase', 'default')]))); + out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].accessors.map((attr: any) => attr[vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('modelAccessorCase', 'snake')]), vscode.CompletionItemKind.Constant)); + out = out.concat(this.getCompletionItems(document, position, this.models[modelClass].relations, vscode.CompletionItemKind.Value)); + } + return out; + } + + getCompletionItems(document: vscode.TextDocument, position: vscode.Position, items: Array, type: vscode.CompletionItemKind = vscode.CompletionItemKind.Property) : Array { + let out: Array = []; + for (let item of items) { + var completeItem = new vscode.CompletionItem(item, type); + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(completeItem); + } + return out; + } + + onChange() { + var self = this; + if (self.timer !== null) { + clearTimeout(self.timer); + } + self.timer = setTimeout(function () { + self.loadModels(); + self.timer = null; + }, 5000); + } + + loadModels() { + var self = this; + try { + Helpers.runLaravel( + "foreach (['" + vscode.workspace.getConfiguration("LaravelExtraIntellisense").get>('modelsPaths', ['app', 'app/Models']).join('\', \'') + "'] as $modelPath) {" + + " if (is_dir(base_path($modelPath))) {" + + " foreach (scandir(base_path($modelPath)) as $sourceFile) {" + + " if (substr($sourceFile, -4) == '.php' && is_file(base_path(\"$modelPath/$sourceFile\"))) {" + + " include_once base_path(\"$modelPath/$sourceFile\");" + + " }" + + " }" + + " }" + + "}" + + "$modelClasses = array_values(array_filter(get_declared_classes(), function ($declaredClass) {" + + " return is_subclass_of($declaredClass, 'Illuminate\\Database\\Eloquent\\Model') && $declaredClass != 'Illuminate\\Database\\Eloquent\\Relations\\Pivot' && $declaredClass != 'Illuminate\\Foundation\\Auth\\User';" + + "}));" + + "$output = [];" + + "foreach ($modelClasses as $modelClass) {" + + " $classReflection = new \\ReflectionClass($modelClass);" + + " $output[$modelClass] = [" + + " 'name' => $classReflection->getShortName()," + + " 'camelCase' => Illuminate\\Support\\Str::camel($classReflection->getShortName())," + + " 'snakeCase' => Illuminate\\Support\\Str::snake($classReflection->getShortName())," + + " 'pluralCamelCase' => Illuminate\\Support\\Str::camel(Illuminate\\Support\\Str::plural($classReflection->getShortName()))," + + " 'pluralSnakeCase' => Illuminate\\Support\\Str::snake(Illuminate\\Support\\Str::plural($classReflection->getShortName()))," + + " 'attributes' => []," + + " 'accessors' => []," + + " 'relations' => []" + + " ];" + + " try {" + + " $modelInstance = $modelClass::first();" + + " $attributes = array_values(array_unique(array_merge(app($modelClass)->getFillable(), array_keys($modelInstance ? $modelInstance->getAttributes() : []))));" + + " $output[$modelClass]['attributes'] = array_map(function ($attribute) {" + + " return ['default' => $attribute, 'snake' => Illuminate\\Support\\Str::snake($attribute), 'camel' => Illuminate\\Support\\Str::camel($attribute)];" + + " }, $attributes);" + + " } catch (\\Throwable $e) {}" + + " foreach ($classReflection->getMethods() as $classMethod) {" + + " try {" + + " if (" + + " $classMethod->isStatic() == false &&" + + " $classMethod->isPublic() == true &&" + + " substr($classMethod->getName(), 0, 3) != 'get' &&" + + " substr($classMethod->getName(), 0, 3) != 'set' &&" + + " count($classMethod->getParameters()) == 0 &&" + + " preg_match('/belongsTo|hasMany|hasOne|morphOne|morphMany|morphTo/', implode('', array_slice(file($classMethod->getFileName()), $classMethod->getStartLine(), $classMethod->getEndLine() - $classMethod->getStartLine() - 1)))" + + " ) {" + + " $output[$modelClass]['relations'][] = $classMethod->getName();" + + " } elseif (" + + " substr($classMethod->getName(), 0, 3) == 'get' && " + + " substr($classMethod->getName(), -9) == 'Attribute' &&" + + " !empty(substr($classMethod->getName(), 3, -9))" + + " ) {" + + " $attributeName = substr($classMethod->getName(), 3, -9);" + + " $output[$modelClass]['accessors'][] = ['default' => $attributeName, 'snake' => Illuminate\\Support\\Str::snake($attributeName), 'camel' => Illuminate\\Support\\Str::camel($attributeName)];" + + " }" + + " } catch (\\Throwable $e) {}" + + " }" + + " sort($output[$modelClass]['attributes']);" + + " sort($output[$modelClass]['relations']);" + + "}" + + "echo json_encode($output);", + "Eloquent Attributes and Relations" + ).then(function (result) { + let models = JSON.parse(result); + self.models = models; + }).catch(function (e) { + console.error(e); + }); + } catch (exception) { + console.error(exception); + } + } +} diff --git a/src/MiddlewareProvider.ts b/src/MiddlewareProvider.ts index 2f4a0ff..3061cd4 100644 --- a/src/MiddlewareProvider.ts +++ b/src/MiddlewareProvider.ts @@ -1,67 +1,86 @@ -'use strict'; - -import * as vscode from 'vscode'; -import Helpers from './helpers'; - - -export default class MiddlewareProvider implements vscode.CompletionItemProvider { - private timer: any = null; - private middlewares: Array = []; - private watcher: any = null; - - - constructor () { - var self = this; - self.loadMiddlewares(); - if (vscode.workspace.workspaceFolders !== undefined) { - this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "app/Http/Kernel.php")); - this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); - } - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - if (func === null) { - return out; - } - - if (func.function.includes("middleware")) { - for (let i in this.middlewares) { - var compeleteItem = new vscode.CompletionItem(i, vscode.CompletionItemKind.Enum); - compeleteItem.detail = this.middlewares[i]; - compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(compeleteItem); - } - } - return out; - } - - onChange() { - var self = this; - if (self.timer !== null) { - clearTimeout(self.timer); - } - self.timer = setTimeout(function () { - self.loadMiddlewares(); - self.timer = null; - }, 5000); - } - - loadMiddlewares() { - if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { - try { - var self = this; - // array_map(function ($rh) {return $rh->getName();}, array_filter((new ReflectionMethod('App\Http\Middleware\Authenticate', 'handle'))->getParameters(), function ($rc) {return $rc->getName() != 'request' && $rc->getName() != 'next';})) - Helpers.runLaravel("$middlewares = array_merge(app('Illuminate\\Contracts\\Http\\Kernel')->getMiddlewareGroups(), app('Illuminate\\Contracts\\Http\\Kernel')->getRouteMiddleware());foreach($middlewares as $key => &$value) {if (is_array($value)){$value = null;}else{$parameters = array_filter((new ReflectionMethod($value, 'handle'))->getParameters(), function ($rc) {return $rc->getName() != 'request' && $rc->getName() != 'next';});$value=implode(',', array_map(function ($rh) {return $rh->getName().($rh->isVariadic()?'...':'');}, $parameters));if(empty($value)){$value=null;}};}echo json_encode($middlewares);") - .then(function (result) { - let middlewares = JSON.parse(result); - self.middlewares = middlewares; - }); - } catch (exception) { - console.error(exception); - } - } - } -} - +'use strict'; + +import * as vscode from 'vscode'; +import Helpers from './helpers'; + + +export default class MiddlewareProvider implements vscode.CompletionItemProvider { + private timer: any = null; + private middlewares: Array = []; + private watcher: any = null; + + + constructor () { + var self = this; + self.loadMiddlewares(); + if (vscode.workspace.workspaceFolders !== undefined) { + this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "app/Http/Kernel.php")); + this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); + } + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + if (func === null) { + return out; + } + + if (func.function.includes("middleware")) { + for (let i in this.middlewares) { + var compeleteItem = new vscode.CompletionItem(i, vscode.CompletionItemKind.Enum); + compeleteItem.detail = this.middlewares[i]; + compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(compeleteItem); + } + } + return out; + } + + onChange() { + var self = this; + if (self.timer !== null) { + clearTimeout(self.timer); + } + self.timer = setTimeout(function () { + self.loadMiddlewares(); + self.timer = null; + }, 5000); + } + + loadMiddlewares() { + if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { + try { + var self = this; + // array_map(function ($rh) {return $rh->getName();}, array_filter((new ReflectionMethod('App\Http\Middleware\Authenticate', 'handle'))->getParameters(), function ($rc) {return $rc->getName() != 'request' && $rc->getName() != 'next';})) + Helpers.runLaravel( + "$middlewares = array_merge(app('Illuminate\\Contracts\\Http\\Kernel')->getMiddlewareGroups(), app('Illuminate\\Contracts\\Http\\Kernel')->getRouteMiddleware());" + + "foreach ($middlewares as $key => &$value) {" + + " if (is_array($value)) {" + + " $value = null;" + + " } else {" + + " $parameters = array_filter((new ReflectionMethod($value, 'handle'))->getParameters(), function ($rc) {" + + " return $rc->getName() != 'request' && $rc->getName() != 'next';" + + " });" + + " $value = implode(',', array_map(function ($rh) {" + + " return $rh->getName() . ($rh->isVariadic() ? '...' : '');" + + " }, $parameters));" + + " if (empty($value)) {" + + " $value = null;" + + " }" + + " };" + + "}" + + "echo json_encode($middlewares);", + "Middlewares" + ) + .then(function (result) { + let middlewares = JSON.parse(result); + self.middlewares = middlewares; + }); + } catch (exception) { + console.error(exception); + } + } + } +} + diff --git a/src/RouteProvider.ts b/src/RouteProvider.ts index 43f96a5..cd9b9e6 100644 --- a/src/RouteProvider.ts +++ b/src/RouteProvider.ts @@ -1,151 +1,158 @@ -'use strict'; - -import * as vscode from 'vscode'; -import * as fs from 'fs'; -import Helpers from './helpers'; - - -export default class RouteProvider implements vscode.CompletionItemProvider { - private timer: any = null; - private routes: Array = []; - private controllers: Array = []; - private watcher: any = null; - - - constructor () { - var self = this; - self.loadRoutes(); - self.loadControllers(); - if (vscode.workspace.workspaceFolders !== undefined) { - this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "{,**/}{Controllers,[Rr]oute}{,s}{.php,/*.php,/**/*.php}")); - this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); - this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); - this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); - } - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - if (func === null) { - return out; - } - - if (func && ((func.class && Helpers.tags.route.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.route.functions.some((fn:string) => func.function.includes(fn)))) { - if (func.class === 'Route' && ['get', 'post', 'put', 'patch', 'delete', 'options', 'any', 'match'].some((fc:string) => func.function.includes(fc))) { - if ((func.function === 'match' && func.paramIndex === 2) || (func.function !== 'match' && func.paramIndex === 1)) { - // Route action autocomplete. - for (let i in this.controllers) { - if (typeof this.controllers[i] === "string" && this.controllers[i].length > 0) { - var compeleteItem2 = new vscode.CompletionItem(this.controllers[i], vscode.CompletionItemKind.Enum); - compeleteItem2.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(compeleteItem2); - } - } - } - } else if (func.function.includes('middleware') === false) { - if (func.paramIndex === 1) { - // route parameters autocomplete - for (let i in this.routes) { - if (this.routes[i].name === func.parameters[0]) { - for (var j in this.routes[i].parameters) { - var compeleteItem = new vscode.CompletionItem(this.routes[i].parameters[j], vscode.CompletionItemKind.Variable); - compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(compeleteItem); - } - return out; - } - } - } - - // Route name autocomplete - for (let i in this.routes) { - if (typeof this.routes[i].name === "string" && this.routes[i].name.length > 0) { - var compeleteItem3 = new vscode.CompletionItem(this.routes[i].name, vscode.CompletionItemKind.Enum); - compeleteItem3.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - compeleteItem3.detail = this.routes[i].action + - "\n\n" + - this.routes[i].method + - ":" + - this.routes[i].uri; - out.push(compeleteItem3); - } - } - } - } - return out; - } - - onChange() { - var self = this; - if (self.timer !== null) { - clearTimeout(self.timer); - } - self.timer = setTimeout(function () { - self.loadRoutes(); - self.loadControllers(); - self.timer = null; - }, 5000); - } - - loadRoutes() { - if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { - try { - var self = this; - Helpers.runLaravel("echo json_encode(array_map(function ($route) {return ['method' => implode('|', array_filter($route->methods(), function ($method) {return $method != 'HEAD';})), 'uri' => $route->uri(), 'name' => $route->getName(), 'action' => str_replace('App\\\\Http\\\\Controllers\\\\', '', $route->getActionName()), 'parameters' => $route->parameterNames()];}, app('router')->getRoutes()->getRoutes()));") - .then(function (result) { - var routes = JSON.parse(result); - routes = routes.filter((route: any) => route !== 'null'); - self.routes = routes; - }); - } catch (exception) { - console.error(exception); - } - } - } - - loadControllers() { - try { - this.controllers = this.getControllers(Helpers.projectPath("app/Http/Controllers")).map((contoller) => contoller.replace(/@__invoke/, '')); - this.controllers = this.controllers.filter((v, i, a) => a.indexOf(v) === i); - } catch (exception) { - console.error(exception); - } - } - - getControllers(path: string): Array { - var self = this; - var controllers: Array = []; - if (path.substr(-1) !== '/' && path.substr(-1) !== '\\') { - path += "/"; - } - if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { - fs.readdirSync(path).forEach(function (file) { - if (fs.lstatSync(path+file).isDirectory()) { - controllers = controllers.concat(self.getControllers(path + file + "/")); - } else { - if (file.includes(".php")) { - var controllerContent = fs.readFileSync(path + file, 'utf8'); - if (controllerContent.length < 50000) { - var match = ((/class\s+([A-Za-z0-9_]+)\s+extends\s+.+/g).exec(controllerContent)); - var matchNamespace = ((/namespace .+\\Http\\Controllers\\?([A-Za-z0-9_]*)/g).exec(controllerContent)); - var functionRegex = /public\s+function\s+([A-Za-z0-9_]+)\(.*\)/g; - if (match !== null && matchNamespace) { - var className = match[1]; - var namespace = matchNamespace[1]; - while ((match = functionRegex.exec(controllerContent)) !== null && match[1] !== '__construct') { - if (namespace.length > 0) { - controllers.push(namespace + '\\' + className + '@' + match[1]); - } - controllers.push(className + '@' + match[1]); - } - } - } - } - } - }); - } - return controllers; - } -} - +'use strict'; + +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import Helpers from './helpers'; + + +export default class RouteProvider implements vscode.CompletionItemProvider { + private timer: any = null; + private routes: Array = []; + private controllers: Array = []; + private watcher: any = null; + + + constructor () { + var self = this; + self.loadRoutes(); + self.loadControllers(); + if (vscode.workspace.workspaceFolders !== undefined) { + this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "{,**/}{Controllers,[Rr]oute}{,s}{.php,/*.php,/**/*.php}")); + this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); + this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); + this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); + } + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + if (func === null) { + return out; + } + + if (func && ((func.class && Helpers.tags.route.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.route.functions.some((fn:string) => func.function.includes(fn)))) { + if (func.class === 'Route' && ['get', 'post', 'put', 'patch', 'delete', 'options', 'any', 'match'].some((fc:string) => func.function.includes(fc))) { + if ((func.function === 'match' && func.paramIndex === 2) || (func.function !== 'match' && func.paramIndex === 1)) { + // Route action autocomplete. + for (let i in this.controllers) { + if (typeof this.controllers[i] === "string" && this.controllers[i].length > 0) { + var compeleteItem2 = new vscode.CompletionItem(this.controllers[i], vscode.CompletionItemKind.Enum); + compeleteItem2.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(compeleteItem2); + } + } + } + } else if (func.function.includes('middleware') === false) { + if (func.paramIndex === 1) { + // route parameters autocomplete + for (let i in this.routes) { + if (this.routes[i].name === func.parameters[0]) { + for (var j in this.routes[i].parameters) { + var compeleteItem = new vscode.CompletionItem(this.routes[i].parameters[j], vscode.CompletionItemKind.Variable); + compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(compeleteItem); + } + return out; + } + } + } + + // Route name autocomplete + for (let i in this.routes) { + if (typeof this.routes[i].name === "string" && this.routes[i].name.length > 0) { + var compeleteItem3 = new vscode.CompletionItem(this.routes[i].name, vscode.CompletionItemKind.Enum); + compeleteItem3.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + compeleteItem3.detail = this.routes[i].action + + "\n\n" + + this.routes[i].method + + ":" + + this.routes[i].uri; + out.push(compeleteItem3); + } + } + } + } + return out; + } + + onChange() { + var self = this; + if (self.timer !== null) { + clearTimeout(self.timer); + } + self.timer = setTimeout(function () { + self.loadRoutes(); + self.loadControllers(); + self.timer = null; + }, 5000); + } + + loadRoutes() { + if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { + try { + var self = this; + Helpers.runLaravel( + "echo json_encode(array_map(function ($route) {" + + " return ['method' => implode('|', array_filter($route->methods(), function ($method) {" + + " return $method != 'HEAD';" + + " })), 'uri' => $route->uri(), 'name' => $route->getName(), 'action' => str_replace('App\\\\Http\\\\Controllers\\\\', '', $route->getActionName()), 'parameters' => $route->parameterNames()];" + + "}, app('router')->getRoutes()->getRoutes()));", + "HTTP Routes" + ) + .then(function (result) { + var routes = JSON.parse(result); + routes = routes.filter((route: any) => route !== 'null'); + self.routes = routes; + }); + } catch (exception) { + console.error(exception); + } + } + } + + loadControllers() { + try { + this.controllers = this.getControllers(Helpers.projectPath("app/Http/Controllers")).map((contoller) => contoller.replace(/@__invoke/, '')); + this.controllers = this.controllers.filter((v, i, a) => a.indexOf(v) === i); + } catch (exception) { + console.error(exception); + } + } + + getControllers(path: string): Array { + var self = this; + var controllers: Array = []; + if (path.substr(-1) !== '/' && path.substr(-1) !== '\\') { + path += "/"; + } + if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { + fs.readdirSync(path).forEach(function (file) { + if (fs.lstatSync(path+file).isDirectory()) { + controllers = controllers.concat(self.getControllers(path + file + "/")); + } else { + if (file.includes(".php")) { + var controllerContent = fs.readFileSync(path + file, 'utf8'); + if (controllerContent.length < 50000) { + var match = ((/class\s+([A-Za-z0-9_]+)\s+extends\s+.+/g).exec(controllerContent)); + var matchNamespace = ((/namespace .+\\Http\\Controllers\\?([A-Za-z0-9_]*)/g).exec(controllerContent)); + var functionRegex = /public\s+function\s+([A-Za-z0-9_]+)\(.*\)/g; + if (match !== null && matchNamespace) { + var className = match[1]; + var namespace = matchNamespace[1]; + while ((match = functionRegex.exec(controllerContent)) !== null && match[1] !== '__construct') { + if (namespace.length > 0) { + controllers.push(namespace + '\\' + className + '@' + match[1]); + } + controllers.push(className + '@' + match[1]); + } + } + } + } + } + }); + } + return controllers; + } +} + diff --git a/src/TranslationProvider.ts b/src/TranslationProvider.ts index 3aad984..a842fda 100644 --- a/src/TranslationProvider.ts +++ b/src/TranslationProvider.ts @@ -1,167 +1,167 @@ -'use strict'; - -import * as vscode from 'vscode'; -import * as fs from "fs"; -import Helpers from './helpers'; - - -export default class TranslationProvider implements vscode.CompletionItemProvider { - private timer: any = null; - private translations: Array = []; - private watcher: any = null; - - constructor () { - var self = this; - self.loadTranslations(); - if (vscode.workspace.workspaceFolders !== undefined) { - this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "{,**/}{lang,localization,localizations,trans,translation,translations}/{*,**/*}")); - this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); - this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); - this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); - } - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - if (func === null) { - return out; - } - - if (func && ((func.class && Helpers.tags.trans.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.trans.functions.some((fn:string) => func.function.includes(fn)))) { - if (func.paramIndex === 1) { - // parameters autocomplete - var paramRegex = /\:([A-Za-z0-9_]+)/g; - var match = null; - - for (let i in this.translations) { - if (this.translations[i].name === func.parameters[0]) { - while ((match = paramRegex.exec(this.translations[i].value)) !== null) { - var compeleteItem = new vscode.CompletionItem(match[1], vscode.CompletionItemKind.Variable); - compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(compeleteItem); - } - return out; - } - } - return out; - } - - for (let i in this.translations) { - var completeItem = new vscode.CompletionItem(this.translations[i].name, vscode.CompletionItemKind.Value); - completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - if (this.translations[i].value) { - completeItem.detail = this.translations[i].value.toString(); - } - out.push(completeItem); - } - } - return out; - } - - onChange() { - var self = this; - if (self.timer) { - clearTimeout(self.timer); - } - self.timer = setTimeout(function () { - self.loadTranslations(); - self.timer = null; - }, 5000); - } - - loadTranslations () { - var translations:Array = []; - try { - var self = this; - Helpers.runLaravel("echo json_encode(app('translator')->getLoader()->namespaces());") - .then(async function (result) { - var tranlationNamespaces = JSON.parse(result); - for (let i in tranlationNamespaces) { - tranlationNamespaces[i + '::'] = tranlationNamespaces[i]; - delete tranlationNamespaces[i]; - } - let langPath = JSON.parse(await Helpers.runLaravel('echo json_encode(app()->langPath());')); - tranlationNamespaces[''] = langPath; - var nestedTranslationGroups = function (basePath:string, relativePath: string = '') : Array { - let path = basePath + '/' + relativePath; - let out: Array = []; - if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { - fs.readdirSync(path).forEach(function (file) { - if (fs.lstatSync(path + '/' + file).isFile()) { - out.push(relativePath + '/' + file.replace(/\.php/, '')); - } else if (fs.lstatSync(path + '/' + file).isDirectory()) { - let nestedOut = nestedTranslationGroups(basePath, (relativePath.length > 0 ? relativePath + '/' : '') + file); - for (let nested of nestedOut) { - out.push(nested); - } - } - }); - } - return out; - } - var translationGroups:any = []; - fs.readdirSync(langPath).forEach(function (langDir) { - var path:any = langPath + '/' + langDir; - if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { - fs.readdirSync(path).forEach(function (subDirectory) { - let subDirectoryPath = path + '/' + subDirectory; - if (fs.existsSync(subDirectoryPath) && fs.lstatSync(subDirectoryPath).isDirectory()) { - let nestedDirectories = nestedTranslationGroups(path, subDirectory); - for (let nestedDirectory of nestedDirectories) { - translationGroups.push(nestedDirectory); - } - } - }); - } - }); - for (let i in tranlationNamespaces) { - if (fs.existsSync(tranlationNamespaces[i]) && fs.lstatSync(tranlationNamespaces[i]).isDirectory()) { - fs.readdirSync(tranlationNamespaces[i]).forEach(function (langDir) { - var path:any = tranlationNamespaces[i] + '/' + langDir; - if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { - fs.readdirSync(path).forEach(function (file) { - if (fs.lstatSync(path + '/' + file).isFile()) { - translationGroups.push(i + file.replace(/\.php/, '')); - } - }); - } - }); - } - } - translationGroups = translationGroups.filter(function(item:any, index:any, array:any){ return array.indexOf(item) === index; }); - Helpers.runLaravel("echo json_encode([" + translationGroups.map((transGroup: string) => "'" + transGroup + "' => __('" + transGroup + "')").join(",") + "]);") - .then(function (translationGroupsResult) { - translationGroups = JSON.parse(translationGroupsResult); - for(var i in translationGroups) { - translations = translations.concat(self.getTranslations(translationGroups[i], i)); - } - self.translations = translations; - Helpers.runLaravel("echo json_encode(__('*'));") - .then(function (jsontransResult) { - translations = translations.concat(self.getTranslations(JSON.parse(jsontransResult), '').map(function (transInfo) { - transInfo.name = transInfo.name.replace(/^\./, ''); - return transInfo; - })); - self.translations = translations; - }); - }); - }); - } catch (exception) { - console.error(exception); - } - } - - getTranslations(translations: Array, prefix: string): Array { - var out: Array = []; - for (var i in translations) { - if (translations[i] instanceof Object) { - out.push({name: prefix + '.' + i, value: "array(...)"}); - out = out.concat(this.getTranslations(translations[i], prefix + '.' + i)); - } else { - out.push({name: prefix + '.' + i, value: translations[i]}); - } - } - return out; - } -} +'use strict'; + +import * as vscode from 'vscode'; +import * as fs from "fs"; +import Helpers from './helpers'; + + +export default class TranslationProvider implements vscode.CompletionItemProvider { + private timer: any = null; + private translations: Array = []; + private watcher: any = null; + + constructor () { + var self = this; + self.loadTranslations(); + if (vscode.workspace.workspaceFolders !== undefined) { + this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "{,**/}{lang,localization,localizations,trans,translation,translations}/{*,**/*}")); + this.watcher.onDidChange((e: vscode.Uri) => this.onChange()); + this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); + this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); + } + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + if (func === null) { + return out; + } + + if (func && ((func.class && Helpers.tags.trans.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.trans.functions.some((fn:string) => func.function.includes(fn)))) { + if (func.paramIndex === 1) { + // parameters autocomplete + var paramRegex = /\:([A-Za-z0-9_]+)/g; + var match = null; + + for (let i in this.translations) { + if (this.translations[i].name === func.parameters[0]) { + while ((match = paramRegex.exec(this.translations[i].value)) !== null) { + var compeleteItem = new vscode.CompletionItem(match[1], vscode.CompletionItemKind.Variable); + compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(compeleteItem); + } + return out; + } + } + return out; + } + + for (let i in this.translations) { + var completeItem = new vscode.CompletionItem(this.translations[i].name, vscode.CompletionItemKind.Value); + completeItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + if (this.translations[i].value) { + completeItem.detail = this.translations[i].value.toString(); + } + out.push(completeItem); + } + } + return out; + } + + onChange() { + var self = this; + if (self.timer) { + clearTimeout(self.timer); + } + self.timer = setTimeout(function () { + self.loadTranslations(); + self.timer = null; + }, 5000); + } + + loadTranslations () { + var translations:Array = []; + try { + var self = this; + Helpers.runLaravel("echo json_encode(app('translator')->getLoader()->namespaces());", "Translation namespaces") + .then(async function (result) { + var tranlationNamespaces = JSON.parse(result); + for (let i in tranlationNamespaces) { + tranlationNamespaces[i + '::'] = tranlationNamespaces[i]; + delete tranlationNamespaces[i]; + } + let langPath = JSON.parse(await Helpers.runLaravel('echo json_encode(app()->langPath());', "Translation Path")); + tranlationNamespaces[''] = langPath; + var nestedTranslationGroups = function (basePath:string, relativePath: string = '') : Array { + let path = basePath + '/' + relativePath; + let out: Array = []; + if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { + fs.readdirSync(path).forEach(function (file) { + if (fs.lstatSync(path + '/' + file).isFile()) { + out.push(relativePath + '/' + file.replace(/\.php/, '')); + } else if (fs.lstatSync(path + '/' + file).isDirectory()) { + let nestedOut = nestedTranslationGroups(basePath, (relativePath.length > 0 ? relativePath + '/' : '') + file); + for (let nested of nestedOut) { + out.push(nested); + } + } + }); + } + return out; + } + var translationGroups:any = []; + fs.readdirSync(langPath).forEach(function (langDir) { + var path:any = langPath + '/' + langDir; + if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { + fs.readdirSync(path).forEach(function (subDirectory) { + let subDirectoryPath = path + '/' + subDirectory; + if (fs.existsSync(subDirectoryPath) && fs.lstatSync(subDirectoryPath).isDirectory()) { + let nestedDirectories = nestedTranslationGroups(path, subDirectory); + for (let nestedDirectory of nestedDirectories) { + translationGroups.push(nestedDirectory); + } + } + }); + } + }); + for (let i in tranlationNamespaces) { + if (fs.existsSync(tranlationNamespaces[i]) && fs.lstatSync(tranlationNamespaces[i]).isDirectory()) { + fs.readdirSync(tranlationNamespaces[i]).forEach(function (langDir) { + var path:any = tranlationNamespaces[i] + '/' + langDir; + if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { + fs.readdirSync(path).forEach(function (file) { + if (fs.lstatSync(path + '/' + file).isFile()) { + translationGroups.push(i + file.replace(/\.php/, '')); + } + }); + } + }); + } + } + translationGroups = translationGroups.filter(function(item:any, index:any, array:any){ return array.indexOf(item) === index; }); + Helpers.runLaravel("echo json_encode([" + translationGroups.map((transGroup: string) => "'" + transGroup + "' => __('" + transGroup + "')").join(",") + "]);", "Translations inside namespaces") + .then(function (translationGroupsResult) { + translationGroups = JSON.parse(translationGroupsResult); + for(var i in translationGroups) { + translations = translations.concat(self.getTranslations(translationGroups[i], i)); + } + self.translations = translations; + Helpers.runLaravel("echo json_encode(__('*'));", "Default path Translations") + .then(function (jsontransResult) { + translations = translations.concat(self.getTranslations(JSON.parse(jsontransResult), '').map(function (transInfo) { + transInfo.name = transInfo.name.replace(/^\./, ''); + return transInfo; + })); + self.translations = translations; + }); + }); + }); + } catch (exception) { + console.error(exception); + } + } + + getTranslations(translations: Array, prefix: string): Array { + var out: Array = []; + for (var i in translations) { + if (translations[i] instanceof Object) { + out.push({name: prefix + '.' + i, value: "array(...)"}); + out = out.concat(this.getTranslations(translations[i], prefix + '.' + i)); + } else { + out.push({name: prefix + '.' + i, value: translations[i]}); + } + } + return out; + } +} diff --git a/src/ViewProvider.ts b/src/ViewProvider.ts index 1b54e6d..caca1bd 100644 --- a/src/ViewProvider.ts +++ b/src/ViewProvider.ts @@ -1,154 +1,154 @@ -'use strict'; - -import * as vscode from 'vscode'; -import * as fs from "fs"; -import Helpers from './helpers'; - -export default class ViewProvider implements vscode.CompletionItemProvider { - private timer: any = null; - private views: {[key:string]: string} = {}; - private watcher: any = null; - - constructor () { - var self = this; - self.loadViews(); - if (vscode.workspace.workspaceFolders !== undefined) { - this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "{,**/}{view,views}/{*,**/*}")); - this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); - this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); - } - } - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { - var out:Array = []; - var func = Helpers.parseDocumentFunction(document, position); - if (func === null) { - return out; - } - - if (func && ((func.class && Helpers.tags.view.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.view.functions.some((fn:string) => func.function.includes(fn)))) { - if (func.paramIndex === 0) { - for (let i in this.views) { - var compeleteItem = new vscode.CompletionItem(i, vscode.CompletionItemKind.Constant); - compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(compeleteItem); - } - } else if (typeof this.views[func.parameters[0]] !== 'undefined') { - var viewContent = fs.readFileSync(this.views[func.parameters[0]], 'utf8'); - var variableRegex = /\$([A-Za-z_][A-Za-z0-9_]*)/g; - var r:any = []; - var variableNames = []; - while (r = variableRegex.exec(viewContent)) { - variableNames.push(r[1]); - } - variableNames = variableNames.filter((v, i, a) => a.indexOf(v) === i); - for (let i in variableNames) { - var variableCompeleteItem = new vscode.CompletionItem(variableNames[i], vscode.CompletionItemKind.Constant); - variableCompeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); - out.push(variableCompeleteItem); - } - } - } else if (func && (func.function === '@section' || func.function === '@push')) { - out = this.getYields(func.function, document.getText()); - } - return out; - } - - getYields(func:string, documentText: string): Array { - var out: Array = []; - var extendsRegex = /@extends\s*\([\'\"](.+)[\'\"]\)/g; - var regexResult:any = []; - if (regexResult = extendsRegex.exec(documentText)) { - if (typeof this.views[regexResult[1]] !== 'undefined') { - var parentContent = fs.readFileSync(this.views[regexResult[1]], 'utf8'); - var yieldRegex = /@yield\s*\([\'\"]([A-Za-z0-9_\-\.]+)[\'\"](,.*)?\)/g; - if (func === '@push') { - yieldRegex = /@stack\s*\([\'\"]([A-Za-z0-9_\-\.]+)[\'\"](,.*)?\)/g; - } - var yeildNames = []; - while (regexResult = yieldRegex.exec(parentContent)) { - yeildNames.push(regexResult[1]); - } - yeildNames = yeildNames.filter((v, i, a) => a.indexOf(v) === i); - for (var i in yeildNames) { - var yieldCompeleteItem = new vscode.CompletionItem(yeildNames[i], vscode.CompletionItemKind.Constant); - out.push(yieldCompeleteItem); - } - out = out.concat(this.getYields(func, parentContent)); - } - } - return out; - } - - onChange() { - var self = this; - if (self.timer) { - clearTimeout(self.timer); - } - self.timer = setTimeout(function () { - self.loadViews(); - self.timer = null; - }, 5000); - } - - loadViews () { - try { - var self = this; - var code = "echo json_encode(app('view')->getFinder()->getHints());"; - Helpers.runLaravel(code.replace("getHints", "getPaths")) - .then(function (viewPathsResult) { - var viewPaths = JSON.parse(viewPathsResult); - Helpers.runLaravel(code) - .then(function (viewNamespacesResult) { - var viewNamespaces = JSON.parse(viewNamespacesResult); - for (let i in viewPaths) { - viewPaths[i] = viewPaths[i].replace(Helpers.projectPath('/', true), Helpers.projectPath('/')); - } - for (let i in viewNamespaces) { - for (let j in viewNamespaces[i]) { - viewNamespaces[i][j] = viewNamespaces[i][j].replace(Helpers.projectPath('/', true), Helpers.projectPath('/')); - } - } - let views:any = {}; - for (let i in viewPaths) { - views = Object.assign(views, self.getViews(viewPaths[i])); - } - for (let i in viewNamespaces) { - for (var j in viewNamespaces[i]) { - var viewsInNamespace = self.getViews(viewNamespaces[i][j]); - for (var k in viewsInNamespace) { - views[i + "::" + k] = viewNamespaces[k]; - } - } - } - self.views = views; - }); - }); - } catch (exception) { - console.error(exception); - } - } - - getViews(path: string): {[key:string]: string} { - if (path.substr(-1) !== '/' && path.substr(-1) !== '\\') { - path += "/"; - } - var out: {[key:string]: string} = {}; - var self = this; - if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { - fs.readdirSync(path).forEach(function (file) { - if (fs.lstatSync(path+file).isDirectory()) { - var viewsInDirectory = self.getViews(path + file + "/"); - for (var i in viewsInDirectory) { - out[file + vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('viewDirectorySeparator') + i] = viewsInDirectory[i]; - } - } else { - if (file.includes("blade.php")) { - out[file.replace(".blade.php", "")] = path + file; - } - } - }); - } - return out; - } -} +'use strict'; + +import * as vscode from 'vscode'; +import * as fs from "fs"; +import Helpers from './helpers'; + +export default class ViewProvider implements vscode.CompletionItemProvider { + private timer: any = null; + private views: {[key:string]: string} = {}; + private watcher: any = null; + + constructor () { + var self = this; + self.loadViews(); + if (vscode.workspace.workspaceFolders !== undefined) { + this.watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], "{,**/}{view,views}/{*,**/*}")); + this.watcher.onDidCreate((e: vscode.Uri) => this.onChange()); + this.watcher.onDidDelete((e: vscode.Uri) => this.onChange()); + } + } + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Array { + var out:Array = []; + var func = Helpers.parseDocumentFunction(document, position); + if (func === null) { + return out; + } + + if (func && ((func.class && Helpers.tags.view.classes.some((cls:string) => func.class.includes(cls))) || Helpers.tags.view.functions.some((fn:string) => func.function.includes(fn)))) { + if (func.paramIndex === 0) { + for (let i in this.views) { + var compeleteItem = new vscode.CompletionItem(i, vscode.CompletionItemKind.Constant); + compeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(compeleteItem); + } + } else if (typeof this.views[func.parameters[0]] !== 'undefined') { + var viewContent = fs.readFileSync(this.views[func.parameters[0]], 'utf8'); + var variableRegex = /\$([A-Za-z_][A-Za-z0-9_]*)/g; + var r:any = []; + var variableNames = []; + while (r = variableRegex.exec(viewContent)) { + variableNames.push(r[1]); + } + variableNames = variableNames.filter((v, i, a) => a.indexOf(v) === i); + for (let i in variableNames) { + var variableCompeleteItem = new vscode.CompletionItem(variableNames[i], vscode.CompletionItemKind.Constant); + variableCompeleteItem.range = document.getWordRangeAtPosition(position, Helpers.wordMatchRegex); + out.push(variableCompeleteItem); + } + } + } else if (func && (func.function === '@section' || func.function === '@push')) { + out = this.getYields(func.function, document.getText()); + } + return out; + } + + getYields(func:string, documentText: string): Array { + var out: Array = []; + var extendsRegex = /@extends\s*\([\'\"](.+)[\'\"]\)/g; + var regexResult:any = []; + if (regexResult = extendsRegex.exec(documentText)) { + if (typeof this.views[regexResult[1]] !== 'undefined') { + var parentContent = fs.readFileSync(this.views[regexResult[1]], 'utf8'); + var yieldRegex = /@yield\s*\([\'\"]([A-Za-z0-9_\-\.]+)[\'\"](,.*)?\)/g; + if (func === '@push') { + yieldRegex = /@stack\s*\([\'\"]([A-Za-z0-9_\-\.]+)[\'\"](,.*)?\)/g; + } + var yeildNames = []; + while (regexResult = yieldRegex.exec(parentContent)) { + yeildNames.push(regexResult[1]); + } + yeildNames = yeildNames.filter((v, i, a) => a.indexOf(v) === i); + for (var i in yeildNames) { + var yieldCompeleteItem = new vscode.CompletionItem(yeildNames[i], vscode.CompletionItemKind.Constant); + out.push(yieldCompeleteItem); + } + out = out.concat(this.getYields(func, parentContent)); + } + } + return out; + } + + onChange() { + var self = this; + if (self.timer) { + clearTimeout(self.timer); + } + self.timer = setTimeout(function () { + self.loadViews(); + self.timer = null; + }, 5000); + } + + loadViews () { + try { + var self = this; + var code = "echo json_encode(app('view')->getFinder()->getHints());"; + Helpers.runLaravel(code.replace("getHints", "getPaths"), "Views paths") + .then(function (viewPathsResult) { + var viewPaths = JSON.parse(viewPathsResult); + Helpers.runLaravel(code, "Views") + .then(function (viewNamespacesResult) { + var viewNamespaces = JSON.parse(viewNamespacesResult); + for (let i in viewPaths) { + viewPaths[i] = viewPaths[i].replace(Helpers.projectPath('/', true), Helpers.projectPath('/')); + } + for (let i in viewNamespaces) { + for (let j in viewNamespaces[i]) { + viewNamespaces[i][j] = viewNamespaces[i][j].replace(Helpers.projectPath('/', true), Helpers.projectPath('/')); + } + } + let views:any = {}; + for (let i in viewPaths) { + views = Object.assign(views, self.getViews(viewPaths[i])); + } + for (let i in viewNamespaces) { + for (var j in viewNamespaces[i]) { + var viewsInNamespace = self.getViews(viewNamespaces[i][j]); + for (var k in viewsInNamespace) { + views[i + "::" + k] = viewNamespaces[k]; + } + } + } + self.views = views; + }); + }); + } catch (exception) { + console.error(exception); + } + } + + getViews(path: string): {[key:string]: string} { + if (path.substr(-1) !== '/' && path.substr(-1) !== '\\') { + path += "/"; + } + var out: {[key:string]: string} = {}; + var self = this; + if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { + fs.readdirSync(path).forEach(function (file) { + if (fs.lstatSync(path+file).isDirectory()) { + var viewsInDirectory = self.getViews(path + file + "/"); + for (var i in viewsInDirectory) { + out[file + vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('viewDirectorySeparator') + i] = viewsInDirectory[i]; + } + } else { + if (file.includes("blade.php")) { + out[file.replace(".blade.php", "")] = path + file; + } + } + }); + } + return out; + } +} diff --git a/src/extension.ts b/src/extension.ts index 29f72c9..5e16371 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,86 +1,86 @@ -'use strict'; - -import * as vscode from 'vscode'; -import * as fs from "fs"; -import Helpers from "./helpers"; - -import RouteProvider from "./RouteProvider"; -import ViewProvider from "./ViewProvider"; -import ConfigProvider from './ConfigProvider'; -import TranslationProvider from './TranslationProvider'; -import MixProvider from './MixProvider'; -import ValidationProvider from './ValidationProvider'; -import EnvProvider from './EnvProvider'; -import MiddlewareProvider from './MiddlewareProvider'; -import AuthProvider from './AuthProvider'; -import AssetProvider from './AssetProvider'; -import EloquentProvider from './EloquentProvider'; -import BladeProvider from './BladeProvider'; - - -export function activate(context: vscode.ExtensionContext) { - showWelcomeMessage(context); - if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { - if (fs.existsSync(Helpers.projectPath("artisan"))) { - if (Helpers.outputChannel === null) { - Helpers.outputChannel = vscode.window.createOutputChannel("Laravel Extra Intellisense"); - Helpers.outputChannel.appendLine("Laravel Extra Intellisense Started..."); - } - - const LANGUAGES = - [ - { scheme: 'file', language: 'php' }, - { scheme: 'file', language: 'blade' }, - { scheme: 'file', language: 'laravel-blade' } - ]; - const TRIGGER_CHARACTERS = "\"'".split(""); - - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new RouteProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new ViewProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new ConfigProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new TranslationProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new MixProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new ValidationProvider, ...TRIGGER_CHARACTERS.concat(['|']))); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new EnvProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new MiddlewareProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new AuthProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new AssetProvider, ...TRIGGER_CHARACTERS)); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new EloquentProvider, ...TRIGGER_CHARACTERS.concat(['>']))); - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new BladeProvider, '@')); - } - } -} - -export function deactivate() {} - -function showWelcomeMessage(context: vscode.ExtensionContext) { - let previousVersion = context.globalState.get('laravel-extra-intellisense-version'); - let currentVersion = vscode.extensions.getExtension('amiralizadeh9480.laravel-extra-intellisense')?.packageJSON?.version; - let message : string | null = null; - let previousVersionArray = previousVersion ? previousVersion.split('.').map((s: string) => Number(s)) : [0, 0, 0]; - let currentVersionArray = currentVersion.split('.').map((s: string) => Number(s)); - if (previousVersion === undefined || previousVersion.length === 0) { - message = "Thanks for using Laravel Extra Intellisense."; - } else if (currentVersion !== previousVersion && ( - // (previousVersionArray[0] === currentVersionArray[0] && previousVersionArray[1] === currentVersionArray[1] && previousVersionArray[2] < currentVersionArray[2]) || - (previousVersionArray[0] === currentVersionArray[0] && previousVersionArray[1] < currentVersionArray[1]) || - (previousVersionArray[0] < currentVersionArray[0]) - ) - ) { - message = "Laravel Extra Intellisense updated to " + currentVersion + " - New feature: Add blade directives autocomplete."; - } - if (message) { - vscode.window.showInformationMessage(message, '⭐️ Star on Github', '🐞 Report Bug') - .then(function (val: string | undefined) { - if (val === '⭐️ Rate') { - vscode.env.openExternal(vscode.Uri.parse('https://marketplace.visualstudio.com/items?itemName=amiralizadeh9480.laravel-extra-intellisense')); - } else if (val === '🐞 Report Bug') { - vscode.env.openExternal(vscode.Uri.parse('https://github.com/amir9480/vscode-laravel-extra-intellisense/issues')); - } else if (val === '⭐️ Star on Github') { - vscode.env.openExternal(vscode.Uri.parse('https://github.com/amir9480/vscode-laravel-extra-intellisense')); - } - }); - context.globalState.update('laravel-extra-intellisense-version', currentVersion); - } -} - +'use strict'; + +import * as vscode from 'vscode'; +import * as fs from "fs"; +import Helpers from "./helpers"; + +import RouteProvider from "./RouteProvider"; +import ViewProvider from "./ViewProvider"; +import ConfigProvider from './ConfigProvider'; +import TranslationProvider from './TranslationProvider'; +import MixProvider from './MixProvider'; +import ValidationProvider from './ValidationProvider'; +import EnvProvider from './EnvProvider'; +import MiddlewareProvider from './MiddlewareProvider'; +import AuthProvider from './AuthProvider'; +import AssetProvider from './AssetProvider'; +import EloquentProvider from './EloquentProvider'; +import BladeProvider from './BladeProvider'; + + +export function activate(context: vscode.ExtensionContext) { + showWelcomeMessage(context); + if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { + if (fs.existsSync(Helpers.projectPath("artisan"))) { + if (Helpers.outputChannel === null) { + Helpers.outputChannel = vscode.window.createOutputChannel("Laravel Extra Intellisense", {log: true}); + Helpers.outputChannel.info("Laravel Extra Intellisense Started..."); + } + + const LANGUAGES = + [ + { scheme: 'file', language: 'php' }, + { scheme: 'file', language: 'blade' }, + { scheme: 'file', language: 'laravel-blade' } + ]; + const TRIGGER_CHARACTERS = "\"'".split(""); + + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new RouteProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new ViewProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new ConfigProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new TranslationProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new MixProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new ValidationProvider, ...TRIGGER_CHARACTERS.concat(['|']))); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new EnvProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new MiddlewareProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new AuthProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new AssetProvider, ...TRIGGER_CHARACTERS)); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new EloquentProvider, ...TRIGGER_CHARACTERS.concat(['>']))); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(LANGUAGES, new BladeProvider, '@')); + } + } +} + +export function deactivate() {} + +function showWelcomeMessage(context: vscode.ExtensionContext) { + let previousVersion = context.globalState.get('laravel-extra-intellisense-version'); + let currentVersion = vscode.extensions.getExtension('amiralizadeh9480.laravel-extra-intellisense')?.packageJSON?.version; + let message : string | null = null; + let previousVersionArray = previousVersion ? previousVersion.split('.').map((s: string) => Number(s)) : [0, 0, 0]; + let currentVersionArray = currentVersion.split('.').map((s: string) => Number(s)); + if (previousVersion === undefined || previousVersion.length === 0) { + message = "Thanks for using Laravel Extra Intellisense."; + } else if (currentVersion !== previousVersion && ( + // (previousVersionArray[0] === currentVersionArray[0] && previousVersionArray[1] === currentVersionArray[1] && previousVersionArray[2] < currentVersionArray[2]) || + (previousVersionArray[0] === currentVersionArray[0] && previousVersionArray[1] < currentVersionArray[1]) || + (previousVersionArray[0] < currentVersionArray[0]) + ) + ) { + message = "Laravel Extra Intellisense updated to " + currentVersion + " - New feature: Add blade directives autocomplete."; + } + if (message) { + vscode.window.showInformationMessage(message, '⭐️ Star on Github', '🐞 Report Bug') + .then(function (val: string | undefined) { + if (val === '⭐️ Rate') { + vscode.env.openExternal(vscode.Uri.parse('https://marketplace.visualstudio.com/items?itemName=amiralizadeh9480.laravel-extra-intellisense')); + } else if (val === '🐞 Report Bug') { + vscode.env.openExternal(vscode.Uri.parse('https://github.com/amir9480/vscode-laravel-extra-intellisense/issues')); + } else if (val === '⭐️ Star on Github') { + vscode.env.openExternal(vscode.Uri.parse('https://github.com/amir9480/vscode-laravel-extra-intellisense')); + } + }); + context.globalState.update('laravel-extra-intellisense-version', currentVersion); + } +} + diff --git a/src/helpers.ts b/src/helpers.ts index 5df5262..acb3e36 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,322 +1,353 @@ -'use strict'; - -import * as vscode from 'vscode'; -import * as cp from 'child_process'; -import * as fs from 'fs'; -import * as os from 'os'; -import { resolve } from 'path'; - -export default class Helpers { - - static wordMatchRegex = /[\w\d\-_\.\:\\\/@]+/g; - static phpParser:any = null; - static cachedParseFunction:any = null; - static modelsCache: Array; - static modelsCacheTime: number = 0; - static outputChannel: vscode.OutputChannel|null = null; - - static tags:any = { - config: {classes: ['Config'] , functions: ['config']}, - mix: {classes: [] , functions: ['mix']}, - route: {classes: ['Route'] , functions: ['route', 'signedRoute']}, - trans: {classes: ['Lang'] , functions: ['__', 'trans', '@lang']}, - validation: {classes: ['Validator'] , functions: ['validate', 'sometimes', 'rules']}, - view: {classes: ['View'] , functions: ['view', 'markdown', 'links', '@extends', '@component', '@include', '@each']}, - env: {classes: [] , functions: ['env']}, - auth: {classes: ['Gate'] , functions: ['can', '@can', '@cannot', '@canany']}, - asset: {classes: [] , functions: ['asset']}, - model: {classes: [] , functions: []}, - }; - static functionRegex: any = null; - - static relationMethods = ['has', 'orHas', 'whereHas', 'orWhereHas', 'whereDoesntHave', 'orWhereDoesntHave', - 'doesntHave', 'orDoesntHave', 'hasMorph', 'orHasMorph', 'doesntHaveMorph', 'orDoesntHaveMorph', - 'whereHasMorph', 'orWhereHasMorph', 'whereDoesntHaveMorph', 'orWhereDoesntHaveMorph', - 'withAggregate', 'withCount', 'withMax', 'withMin', 'withSum', 'withAvg']; - - /** - * Create full path from project file name - * - * @param path - * @param forCode - * @param string - */ - static projectPath(path:string, forCode: boolean = false) : string { - if (path[0] !== '/') { - path = '/' + path; - } - - let basePath = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('basePath'); - if (forCode === false && basePath && basePath.length > 0) { - if (basePath.startsWith('.') && vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { - basePath = resolve(vscode.workspace.workspaceFolders[0].uri.fsPath, basePath); - } - basePath = basePath.replace(/[\/\\]$/, ""); - return basePath + path; - } - - let basePathForCode = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('basePathForCode'); - if (forCode && basePathForCode && basePathForCode.length > 0) { - if (basePathForCode.startsWith('.') && vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { - basePathForCode = resolve(vscode.workspace.workspaceFolders[0].uri.fsPath, basePathForCode); - } - basePathForCode = basePathForCode.replace(/[\/\\]$/, ""); - return basePathForCode + path; - } - - if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { - for (let workspaceFolder of vscode.workspace.workspaceFolders) { - if (fs.existsSync(workspaceFolder.uri.fsPath + "/artisan")) { - return workspaceFolder.uri.fsPath + path; - } - } - } - return ""; - } - - static arrayUnique(value:any, index:any, self:Array) { - return self.indexOf(value) === index; - } - - /** - * Boot laravel and run simple php code. - * - * @param code - */ - static runLaravel(code: string) : Promise { - code = code.replace(/(?:\r\n|\r|\n)/g, ' '); - if (fs.existsSync(Helpers.projectPath("vendor/autoload.php")) && fs.existsSync(Helpers.projectPath("bootstrap/app.php"))) { - var command = - "define('LARAVEL_START', microtime(true));" + - "require_once '" + Helpers.projectPath("vendor/autoload.php", true) + "';" + - "$app = require_once '" + Helpers.projectPath("bootstrap/app.php", true) + "';" + - "class VscodeLaravelExtraIntellisenseProvider extends \\Illuminate\\Support\\ServiceProvider" + - "{" + - " public function register() {}" + - " public function boot()" + - " {" + - " if (method_exists($this->app['log'], 'setHandlers')) {" + - " $this->app['log']->setHandlers([new \\Monolog\\Handler\\NullHandler()]);" + - " }" + - " }" + - "}" + - "$app->register(new VscodeLaravelExtraIntellisenseProvider($app));" + - "$kernel = $app->make(Illuminate\\Contracts\\Console\\Kernel::class);" + - - "$status = $kernel->handle(" + - "$input = new Symfony\\Component\\Console\\Input\\ArgvInput," + - "new Symfony\\Component\\Console\\Output\\ConsoleOutput" + - ");" + - "echo '___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_OUTPUT___';" + - code + - "echo '___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_END_OUTPUT___';"; - - var self = this; - - return new Promise(function (resolve, error) { - self.runPhp(command) - .then(function (result: string) { - var out : string | null | RegExpExecArray = result; - out = /___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_OUTPUT___(.*)___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_END_OUTPUT___/g.exec(out); - if (out) { - resolve(out[1]); - } else { - error("PARSE ERROR: " + result); - } - }) - .catch(function (e : Error) { - error(e); - }); - }); - } - return new Promise((resolve, error) => resolve("")); - } - - /** - * run simple php code. - * - * @param code - */ - static async runPhp(code: string) : Promise { - code = code.replace(/\"/g, "\\\""); - if (['linux', 'openbsd', 'sunos', 'darwin'].some(unixPlatforms => os.platform().includes(unixPlatforms))) { - code = code.replace(/\$/g, "\\$"); - code = code.replace(/\\\\'/g, '\\\\\\\\\''); - code = code.replace(/\\\\"/g, '\\\\\\\\\"'); - } - let command = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('phpCommand') ?? "php -r \"{code}\""; - command = command.replace("{code}", code); - let out = new Promise(function (resolve, error) { - cp.exec(command, - { cwd: vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined }, - function (err, stdout, stderr) { - if (stdout.length > 0) { - resolve(stdout); - } else { - if (Helpers.outputChannel !== null) { - Helpers.outputChannel.appendLine("Laravel extra intellisense Error: " + stderr); - } - error(stderr); - } - } - ); - }); - return out; - } - - /** - * Parse php code with 'php-parser' package. - * @param code - */ - static parsePhp(code: string): any { - if (! Helpers.phpParser) { - var PhpEngine = require('php-parser'); - Helpers.phpParser = new PhpEngine({ - parser: { - extractDoc: true, - php7: true - }, - ast: { - withPositions: true - } - }); - } - try { - return Helpers.phpParser.parseCode(code); - } catch (exception) { - return null; - } - } - - /** - * Convert php variable defination to javascript variable. - * @param code - */ - static evalPhp(code: string): any { - var out = Helpers.parsePhp('= match.index && match[0] && position < match.index + match[0].length) { - if ((match2 = inlineFunctionMatch.exec(match[0])) !== null && typeof match2[3] === 'string' && typeof match[1] === 'string' && typeof match[6] === 'string' && typeof match2[1] === 'string') { - out = this.parseFunction(match2[3], position - (match.index + match[1].length + match[6].length + match2[1].length), level + 1); - } else if (typeof match[1] === 'string' && typeof match[6]=== 'string' && typeof match[7]=== 'string') { - var textParameters = []; - var paramIndex = null; - var paramIndexCounter = 0; - var paramsPosition = position - (match.index + match[1].length + match[6].length); - - var functionInsideParameter; - if (match[7].length >= 4 && (functionInsideParameter = this.parseFunction(match[7], paramsPosition))) { - return functionInsideParameter; - } - - while ((match2 = paramsRegex.exec(match[7])) !== null) { - textParameters.push(match2[3]); - if (paramsPosition >= match2.index && typeof match2[0] === 'string' && paramsPosition <= match2.index + match2[0].length) { - paramIndex = paramIndexCounter; - } - paramIndexCounter++; - } - var functionParametrs = []; - for (let i in textParameters) { - functionParametrs.push(this.evalPhp(textParameters[i])); - } - out = { - class: match[3], - function: match[4], - paramIndex: paramIndex, - parameters: functionParametrs, - textParameters: textParameters - }; - } - if (level === 0) { - Helpers.cachedParseFunction = {text, position, out}; - } - } - } - } - return out; - } - - /** - * Parse php function call from vscode editor. - * - * @param document - * @param position - */ - static parseDocumentFunction(document: vscode.TextDocument, position: vscode.Position) { - var pos = document.offsetAt(position); - return Helpers.parseFunction(document.getText(), pos); - } - - /** - * Get laravel models as array. - * - * @returns array - */ - static getModels() : Promise> { - var self = this; - return new Promise>(function (resolve, reject) { - if (Math.floor(Date.now()/1000) - self.modelsCacheTime < 60) { - return resolve(self.modelsCache); - } else { - Helpers.runLaravel(` - echo json_encode(array_values(array_filter(array_map(function ($name) {return app()->getNamespace().str_replace([app_path().'/', app_path().'\\\\', '.php', '/'], ['', '', '', '\\\\'], $name);}, array_merge(glob(app_path('*')), glob(app_path('Models/*')))), function ($class) { - return class_exists($class) && is_subclass_of($class, 'Illuminate\\\\Database\\\\Eloquent\\\\Model'); - }))); - `).then(function (result) { - var models = JSON.parse(result); - self.modelsCache = models; - resolve(models); - }) - .catch(function (error) { - console.error(error); - resolve([]); - }); - } - }); - } - - /** - * Get indent space based on user configuration - */ - static getSpacer() : string { - const editor = vscode.window.activeTextEditor; - if (editor && editor.options.insertSpaces) { - return ' '.repeat(editor.options.tabSize); - } - return '\t'; - } -} +'use strict'; + +import * as vscode from 'vscode'; +import * as cp from 'child_process'; +import * as fs from 'fs'; +import * as os from 'os'; +import { resolve } from 'path'; + +export default class Helpers { + + static wordMatchRegex = /[\w\d\-_\.\:\\\/@]+/g; + static phpParser:any = null; + static cachedParseFunction:any = null; + static modelsCache: Array; + static modelsCacheTime: number = 0; + static outputChannel: vscode.LogOutputChannel|null = null; + static lastErrorMessage: number = 0; + static disableErrorMessage: boolean = false; + + static tags:any = { + config: {classes: ['Config'] , functions: ['config']}, + mix: {classes: [] , functions: ['mix']}, + route: {classes: ['Route'] , functions: ['route', 'signedRoute']}, + trans: {classes: ['Lang'] , functions: ['__', 'trans', '@lang']}, + validation: {classes: ['Validator'] , functions: ['validate', 'sometimes', 'rules']}, + view: {classes: ['View'] , functions: ['view', 'markdown', 'links', '@extends', '@component', '@include', '@each']}, + env: {classes: [] , functions: ['env']}, + auth: {classes: ['Gate'] , functions: ['can', '@can', '@cannot', '@canany']}, + asset: {classes: [] , functions: ['asset']}, + model: {classes: [] , functions: []}, + }; + static functionRegex: any = null; + + static relationMethods = ['has', 'orHas', 'whereHas', 'orWhereHas', 'whereDoesntHave', 'orWhereDoesntHave', + 'doesntHave', 'orDoesntHave', 'hasMorph', 'orHasMorph', 'doesntHaveMorph', 'orDoesntHaveMorph', + 'whereHasMorph', 'orWhereHasMorph', 'whereDoesntHaveMorph', 'orWhereDoesntHaveMorph', + 'withAggregate', 'withCount', 'withMax', 'withMin', 'withSum', 'withAvg']; + + /** + * Create full path from project file name + * + * @param path + * @param forCode + * @param string + */ + static projectPath(path:string, forCode: boolean = false) : string { + if (path[0] !== '/') { + path = '/' + path; + } + + let basePath = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('basePath'); + if (forCode === false && basePath && basePath.length > 0) { + if (basePath.startsWith('.') && vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + basePath = resolve(vscode.workspace.workspaceFolders[0].uri.fsPath, basePath); + } + basePath = basePath.replace(/[\/\\]$/, ""); + return basePath + path; + } + + let basePathForCode = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('basePathForCode'); + if (forCode && basePathForCode && basePathForCode.length > 0) { + if (basePathForCode.startsWith('.') && vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + basePathForCode = resolve(vscode.workspace.workspaceFolders[0].uri.fsPath, basePathForCode); + } + basePathForCode = basePathForCode.replace(/[\/\\]$/, ""); + return basePathForCode + path; + } + + if (vscode.workspace.workspaceFolders instanceof Array && vscode.workspace.workspaceFolders.length > 0) { + for (let workspaceFolder of vscode.workspace.workspaceFolders) { + if (fs.existsSync(workspaceFolder.uri.fsPath + "/artisan")) { + return workspaceFolder.uri.fsPath + path; + } + } + } + return ""; + } + + static arrayUnique(value:any, index:any, self:Array) { + return self.indexOf(value) === index; + } + + static showErrorPopup() { + if (Helpers.disableErrorMessage == false && Helpers.lastErrorMessage + 10 < Date.now()/1000) { + Helpers.lastErrorMessage = Date.now()/1000; + + vscode.window.showErrorMessage('Laravel Extra Intellisense error', 'View Error', 'Don\'t show again') + .then(function (val: string | undefined) { + if (val === 'Don\'t show again') { + Helpers.disableErrorMessage = true; + } else if (val === 'View Error') { + Helpers.outputChannel?.show(true); + } + }); + } + } + + /** + * Boot laravel and run simple php code. + * + * @param code + */ + static runLaravel(code: string, description: string|null = null) : Promise { + code = code.replace(/(?:\r\n|\r|\n)/g, ' '); + if (fs.existsSync(Helpers.projectPath("vendor/autoload.php")) && fs.existsSync(Helpers.projectPath("bootstrap/app.php"))) { + var command = + "define('LARAVEL_START', microtime(true));" + + "require_once '" + Helpers.projectPath("vendor/autoload.php", true) + "';" + + "$app = require_once '" + Helpers.projectPath("bootstrap/app.php", true) + "';" + + "class VscodeLaravelExtraIntellisenseProvider extends \\Illuminate\\Support\\ServiceProvider" + + "{" + + " public function register() {}" + + " public function boot()" + + " {" + + " if (method_exists($this->app['log'], 'setHandlers')) {" + + " $this->app['log']->setHandlers([new \\Monolog\\Handler\\ProcessHandler()]);" + + " }" + + " }" + + "}" + + "$app->register(new VscodeLaravelExtraIntellisenseProvider($app));" + + "$kernel = $app->make(Illuminate\\Contracts\\Console\\Kernel::class);" + + + "$status = $kernel->handle(" + + "$input = new Symfony\\Component\\Console\\Input\\ArgvInput," + + "new Symfony\\Component\\Console\\Output\\ConsoleOutput" + + ");" + + "if ($status == 0) {" + + " echo '___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_OUTPUT___';" + + code + + " echo '___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_END_OUTPUT___';" + + "}" + + "$kernel->terminate($input, $status);" + + "exit($status);" + + var self = this; + + return new Promise(function (resolve, error) { + self.runPhp(command, description) + .then(function (result: string) { + var out : string | null | RegExpExecArray = result; + out = /___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_OUTPUT___(.*)___VSCODE_LARAVEL_EXTRA_INSTELLISENSE_END_OUTPUT___/g.exec(out); + if (out) { + resolve(out[1]); + } else { + error("PARSE ERROR: " + result); + + Helpers.outputChannel?.error("Laravel Extra Intellisense Parse Error:\n " + (description ?? '') + '\n\n' + result); + Helpers.showErrorPopup(); + } + }) + .catch(function (e : Error) { + error(e); + }); + }); + } + return new Promise((resolve, error) => resolve("")); + } + + /** + * run simple php code. + * + * @param code + */ + static async runPhp(code: string, description: string|null = null) : Promise { + code = code.replace(/\"/g, "\\\""); + if (['linux', 'openbsd', 'sunos', 'darwin'].some(unixPlatforms => os.platform().includes(unixPlatforms))) { + code = code.replace(/\$/g, "\\$"); + code = code.replace(/\\\\'/g, '\\\\\\\\\''); + code = code.replace(/\\\\"/g, '\\\\\\\\\"'); + } + let commandTemplate = vscode.workspace.getConfiguration("LaravelExtraIntellisense").get('phpCommand') ?? "php -r \"{code}\""; + let command = commandTemplate.replace("{code}", code); + let out = new Promise(function (resolve, error) { + if (description != null) { + Helpers.outputChannel?.info("Laravel Extra Intellisense command started: " + description); + } + + cp.exec(command, + { cwd: vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined }, + function (err, stdout, stderr) { + if (err == null) { + if (description != null) { + Helpers.outputChannel?.info("Laravel Extra Intellisense Resolved: " + description); + } + resolve(stdout); + } else { + const errorOutput = stderr.length > 0 ? stderr : stdout; + Helpers.outputChannel?.error("Laravel Extra Intellisense Error:\n " + (description ?? '') + '\n\n' + errorOutput); + Helpers.showErrorPopup(); + error(errorOutput); + } + } + ); + }); + return out; + } + + /** + * Parse php code with 'php-parser' package. + * @param code + */ + static parsePhp(code: string): any { + if (! Helpers.phpParser) { + var PhpEngine = require('php-parser'); + Helpers.phpParser = new PhpEngine({ + parser: { + extractDoc: true, + php7: true + }, + ast: { + withPositions: true + } + }); + } + try { + return Helpers.phpParser.parseCode(code); + } catch (exception) { + return null; + } + } + + /** + * Convert php variable defination to javascript variable. + * @param code + */ + static evalPhp(code: string): any { + var out = Helpers.parsePhp('= match.index && match[0] && position < match.index + match[0].length) { + if ((match2 = inlineFunctionMatch.exec(match[0])) !== null && typeof match2[3] === 'string' && typeof match[1] === 'string' && typeof match[6] === 'string' && typeof match2[1] === 'string') { + out = this.parseFunction(match2[3], position - (match.index + match[1].length + match[6].length + match2[1].length), level + 1); + } else if (typeof match[1] === 'string' && typeof match[6]=== 'string' && typeof match[7]=== 'string') { + var textParameters = []; + var paramIndex = null; + var paramIndexCounter = 0; + var paramsPosition = position - (match.index + match[1].length + match[6].length); + + var functionInsideParameter; + if (match[7].length >= 4 && (functionInsideParameter = this.parseFunction(match[7], paramsPosition))) { + return functionInsideParameter; + } + + while ((match2 = paramsRegex.exec(match[7])) !== null) { + textParameters.push(match2[3]); + if (paramsPosition >= match2.index && typeof match2[0] === 'string' && paramsPosition <= match2.index + match2[0].length) { + paramIndex = paramIndexCounter; + } + paramIndexCounter++; + } + var functionParametrs = []; + for (let i in textParameters) { + functionParametrs.push(this.evalPhp(textParameters[i])); + } + out = { + class: match[3], + function: match[4], + paramIndex: paramIndex, + parameters: functionParametrs, + textParameters: textParameters + }; + } + if (level === 0) { + Helpers.cachedParseFunction = {text, position, out}; + } + } + } + } + return out; + } + + /** + * Parse php function call from vscode editor. + * + * @param document + * @param position + */ + static parseDocumentFunction(document: vscode.TextDocument, position: vscode.Position) { + var pos = document.offsetAt(position); + return Helpers.parseFunction(document.getText(), pos); + } + + /** + * Get laravel models as array. + * + * @returns array + */ + static getModels() : Promise> { + var self = this; + return new Promise>(function (resolve, reject) { + if (Math.floor(Date.now()/1000) - self.modelsCacheTime < 60) { + return resolve(self.modelsCache); + } else { + Helpers.runLaravel(` + echo json_encode(array_values(array_filter(array_map(function ($name) {return app()->getNamespace().str_replace([app_path().'/', app_path().'\\\\', '.php', '/'], ['', '', '', '\\\\'], $name);}, array_merge(glob(app_path('*')), glob(app_path('Models/*')))), function ($class) { + return class_exists($class) && is_subclass_of($class, 'Illuminate\\\\Database\\\\Eloquent\\\\Model'); + }))); + `, "Application Models").then(function (result) { + var models = JSON.parse(result); + self.modelsCache = models; + resolve(models); + }) + .catch(function (error) { + console.error(error); + resolve([]); + }); + } + }); + } + + /** + * Get indent space based on user configuration + */ + static getSpacer() : string { + const editor = vscode.window.activeTextEditor; + if (editor && editor.options.insertSpaces) { + return ' '.repeat(editor.options.tabSize); + } + return '\t'; + } +}