-
Notifications
You must be signed in to change notification settings - Fork 0
/
PhpNamespaceMonkey.py
115 lines (73 loc) · 3.97 KB
/
PhpNamespaceMonkey.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import json, os, re, sublime, sublime_plugin, time
class PhpNamespaceMonkey():
namespaces = {}
def addBoilerplate(self, view):
settings = sublime.load_settings('PhpNamespaceMonkey.sublime-settings')
if not view.file_name() or not self.isPhpClassFile(view.file_name()) or view.size(): return
if time.time() - os.path.getctime(view.file_name()) > 1: return
namespace = self.resolveNamespace(view.file_name())
className = self.resolveClassName(view.file_name())
type = self.resolveType(className)
if not namespace: return
namespaceStyle = settings.get('namespace_style')
declarations = 'declare(strict_types=1);' if settings.get('declare_strict_types') else None
namespace = 'namespace {};'.format(namespace)
boilerplate = list(filter(None, [ '<?php', declarations, namespace ]))
if namespaceStyle == 'same-line':
view.run_command('append', { 'characters': ' '.join(boilerplate) + '\n' })
elif namespaceStyle == 'next-line':
view.run_command('append', { 'characters': '\n'.join(boilerplate) + '\n' })
elif namespaceStyle == 'psr-2':
view.run_command('append', { 'characters': '\n\n'.join(boilerplate) + '\n' })
if settings.get('include_class_definition'):
view.run_command('append', { 'characters': '\n{} {}\n{{\n}}\n'.format(type, className) })
def loadNamespaces(self, view, force = False):
if not view.window(): return
for path in view.window().folders():
if path in self.namespaces and not force: continue
self.namespaces[path] = namespaces = []
composerJsonPath = path + '/composer.json'
if not os.path.isfile(composerJsonPath): continue
composerJson = json.loads(open(composerJsonPath, 'r').read())
if not composerJson['autoload']: continue
for key in [ 'psr-0', 'psr-4' ]:
if not key in composerJson['autoload']: continue
for namespace, paths in composerJson['autoload'][key].items():
if not namespace: continue
if not isinstance(paths, list): paths = [ paths ]
for path in paths:
if not path.endswith('/'): path += '/'
namespaces.append({ 'path': path, 'namespace': namespace })
def isPhpClassFile(self, path):
fileName = path.split('/')[-1]
return len(fileName) > 0 and fileName[0] == fileName[0].upper() and fileName.endswith('.php')
def resolveNamespace(self, path):
for folder, folderNamespaces in self.namespaces.items():
if path.startswith(folder):
path = path.replace(folder, '').lstrip('/')
namespaces = folderNamespaces
break
if not namespaces: return
namespace = next(filter(lambda namespace: path.startswith(namespace['path']), namespaces), None)
if not namespace: return
subnamespace = '\\'.join(path.replace(namespace['path'], '').replace('.php', '').split('/')[:-1])
return re.sub(r"\\$", '', namespace['namespace'] + subnamespace)
def resolveClassName(self, path):
return path.replace('.php', '').split('/')[-1]
def resolveType(self, className):
matches = re.search('(Interface|Trait|Abstract)$', className)
type = matches.group(1).lower() if matches else 'class'
if type == 'abstract': type += ' class'
return type
class PhpNamespaceMonkeyListener(sublime_plugin.EventListener):
def on_activated_async(self, view):
global monkey
monkey.loadNamespaces(view)
monkey.addBoilerplate(view)
class PhpNamespaceMonkeyReloadNamespacesCommand(sublime_plugin.TextCommand):
def run(self, edit):
global monkey
monkey.loadNamespaces(self.view, True)
def description(self):
return "PHP Namespace Monkey: Reload namespaces"
monkey = PhpNamespaceMonkey()