-
Notifications
You must be signed in to change notification settings - Fork 0
/
HDL.py
175 lines (150 loc) · 7.14 KB
/
HDL.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import sublime, sublime_plugin
import re
import string
class VHDLCompletion(sublime_plugin.EventListener):
def on_query_completions(self, view, prefix, locations):
if prefix.lower() == 'entity':
return [('entity instantiate x','entity work.${1:x arg1}${2:(${3})}'), ('entity instantiate y','entity work.y')]
else:
return []
# Scopes to match
GENERIC_LIST = 'meta.block.generic_list.vhdl'
PORT_LIST = 'meta.block.port_list.vhdl'
ENTITY_INSTANTIATION = 'meta.block.entity_instantiation.vhdl'
COMPONENT_INSTANTIATION = 'meta.block.component_instantiation.vhdl'
PARENTHETICAL_LIST = 'meta.block.parenthetical_list.vhdl'
ARCHITECTURE = 'meta.block.architecture'
# Checks to see if the region is within the passed scope
def check_scope(view,region,scope):
current_scope = view.scope_name(region.a)
if re.search(scope, current_scope, re.IGNORECASE) == None:
return False
return True
# Find the maximum length to match pattern with lines, and
# then add spaces to pad out until the maximum length
def line_up(pattern,lines):
# Initialize min/max variables
min = None
max = None
# Find the min/max values for the patterns
for l in lines:
m = re.search( pattern, l, re.IGNORECASE )
if m != None:
if min == None or len(m.group(0)) < min:
min = len(m.group(0))
if max == None or len(m.group(0)) > max:
max = len(m.group(0))
#print 'min: ' + str(min) + ' max: ' + str(max) + ' pattern: ' + pattern
# As long as we found something and they aren't the same...
if min != None and max != None and min != max:
# Add in spaces to get the counts to match
for i,l in enumerate(lines):
m = re.search( pattern, l, re.IGNORECASE)
if m!= None:
extra = max - len(m.group(0))
lines[i] = string.replace(l, m.group(0), m.group(0) + ' '*extra)
# Align the region with the lines based on scope
def align_and_replace(view, edit, region, lines):
# Normalize the leading whitespace for lines which have a word character in them
if check_scope(view, region, ARCHITECTURE) == False:
line_up( '^(\s+)(?=\w)', lines )
else:
line_up( '^(\s+)(?=signal)', lines )
# If region is in a generic or port list, then align first colon
if check_scope(view, region, GENERIC_LIST) or check_scope(view, region, PORT_LIST):
# Normalizing the length between the stuff leading up to the colon, and the stuff after the colon
# ([^:]+):(.+$)
line_up( '([^:]+)(?=:)', lines )
#print "Aligning on first colon"
# If region is in a port list, then align the direction plus the word after the direction
if check_scope(view, region, PORT_LIST):
line_up( '([^:]+:\s*)(?=in|out|inout|buffer)', lines )
line_up( '([^:]+:\s*(in|out|inout|buffer))', lines )
#print "Aligning on in/out/inout/buffer"
# If region is in a generic or port list, then align on :=
if check_scope(view, region, GENERIC_LIST) or check_scope(view, region, PORT_LIST):
line_up( '(.+?)(?=:=)', lines )
#print "Aligning on :="
# If region is in a generic or port map, then align on =>
if check_scope(view, region, COMPONENT_INSTANTIATION) or check_scope(view, region, ENTITY_INSTANTIATION):
line_up( '[^=]+(?==>)', lines )
#print "Aligning on =>"
if check_scope(view, region, ARCHITECTURE):
# Signals
line_up( '(\s*signal[^:]+)(?=:)', lines )
line_up( '(\s*signal.+?)(?=:=)', lines )
# Constants
line_up( '(\s*constant[^:]+)(?=:)', lines )
line_up( '(\s*constant.+?)(?=:=)', lines )
#print 'Aligning architecture signals'
# Concatenate all the lines
newtext = ''
for l in lines:
newtext = newtext + l ;
if l != lines[-1]:
newtext = newtext + '\n'
# Finish the edit
view.replace(edit,region,newtext)
# Align whole file
class HdlAlignFile(sublime_plugin.TextCommand):
def run(self,edit):
regions = self.view.find_by_selector(PARENTHETICAL_LIST)
print 'Found ' + str(len(regions)) + ' parenthetical lists'
for region in regions:
print 'xy: ' + str(self.view.rowcol(region.a))
# if check_scope(self.view, region, GENERIC_LIST):
# print 'Found generic list : ' + str(self.view.rowcol(region.a))
# text = self.view.substr(region)
# lines = re.split('\n',text)
# align_and_replace(self.view, edit, region, lines)
# elif check_scope(self.view, region, PORT_LIST):
# print 'Found port list : ' + str(self.view.rowcol(region.a))
# text = self.view.substr(region)
# lines = re.split('\n',text)
# align_and_replace(self.view, edit, region, lines)
# elif check_scope(self.view, region, ENTITY_INSTANTIATION):
# print 'Found entity inst : ' + str(self.view.rowcol(region.a))
# text = self.view.substr(region)
# lines = re.split('\n',text)
# align_and_replace(self.view, edit, region, lines)
# elif check_scope(self.view, region, COMPONENT_INSTANTIATION):
# print 'Found component inst: ' + str(self.view.rowcol(region.a))
# text = self.view.substr(region)
# lines = re.split('\n',text)
# align_and_replace(self.view, edit, region, lines)
# else:
# print 'Skipping region : ' + str(self.view.rowcol(region.a))
# Align appropriately when carat is in a:
# - generic list
# - port list
# - entity instantiation
# - component instantiation
# - architecture
class HdlAlignRegion(sublime_plugin.TextCommand):
def run(self,edit,cursor=None):
# Find the current carat location(s)
carats = self.view.sel()
# For all the carats...
for carat in carats:
# Extract out the region of the current cursor location
regions = self.view.find_by_selector(PARENTHETICAL_LIST)
region = None
for r in regions:
if r.contains(carat):
region = r
# Make sure we're in a parenthetical list
if region == None:
regions = self.view.find_by_selector(ARCHITECTURE)
for r in regions:
if r.contains(carat):
region = r
if region == None:
print 'Not within a parenthetical list or arhchitecture'
return
# Get the total scope and the text associated with it
scope = self.view.scope_name(region.a)
text = self.view.substr(region)
# Split up the lines based on newlines
lines = re.split('\n',text)
# The main stuff
align_and_replace(self.view, edit, region, lines)