-
Notifications
You must be signed in to change notification settings - Fork 699
/
Outliner.java
225 lines (198 loc) · 6.48 KB
/
Outliner.java
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package org.python.indexer;
import org.python.indexer.types.ModuleType;
import org.python.indexer.types.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* Generates a file outline from the index: a structure representing the
* variable and attribute definitions in a file.
*/
public class Outliner {
public static abstract class Entry {
protected String qname; // entry qualified name
protected int offset; // file offset of referenced declaration
protected Binding.Kind kind; // binding kind of outline entry
public Entry() {
}
public Entry(String qname, int offset, Binding.Kind kind) {
this.qname = qname;
this.offset = offset;
this.kind = kind;
}
public abstract boolean isLeaf();
public Leaf asLeaf() {
return (Leaf)this;
}
public abstract boolean isBranch();
public Branch asBranch() {
return (Branch)this;
}
public abstract boolean hasChildren();
public abstract List<Entry> getChildren();
public abstract void setChildren(List<Entry> children);
public String getQname() {
return qname;
}
public void setQname(String qname) {
if (qname == null) {
throw new IllegalArgumentException("qname param cannot be null");
}
this.qname = qname;
}
/**
* Returns the file offset of the beginning of the identifier referenced
* by this outline entry.
*/
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public Binding.Kind getKind() {
return kind;
}
public void setKind(Binding.Kind kind) {
if (kind == null) {
throw new IllegalArgumentException("kind param cannot be null");
}
this.kind = kind;
}
/**
* Returns the simple (unqualified) name of the identifier.
*/
public String getName() {
String[] parts = qname.split("[.&@%]");
return parts[parts.length-1];
}
@Override public String toString() {
StringBuilder sb = new StringBuilder();
toString(sb, 0);
return sb.toString().trim();
}
public void toString(StringBuilder sb, int depth) {
for (int i = 0; i < depth; i++) {
sb.append(" ");
}
sb.append(getKind());
sb.append(" ");
sb.append(getName());
sb.append("\n");
if (hasChildren()) {
for (Entry e : getChildren()) {
e.toString(sb, depth + 1);
}
}
}
}
/**
* An outline entry with children.
*/
public static class Branch extends Entry {
private List<Entry> children = new ArrayList<Entry>();
public Branch() {
}
public Branch(String qname, int start, Binding.Kind kind) {
super(qname, start, kind);
}
public boolean isLeaf() {
return false;
}
public boolean isBranch() {
return true;
}
public boolean hasChildren() {
return children != null && !children.isEmpty();
}
public List<Entry> getChildren() {
return children;
}
public void setChildren(List<Entry> children) {
this.children = children;
}
}
/**
* An entry with no children.
*/
public static class Leaf extends Entry {
public boolean isLeaf() {
return true;
}
public boolean isBranch() {
return false;
}
public Leaf() {
}
public Leaf(String qname, int start, Binding.Kind kind) {
super(qname, start, kind);
}
public boolean hasChildren() {
return false;
}
public List<Entry> getChildren() {
return new ArrayList<Entry>();
}
public void setChildren(List<Entry> children) {
throw new UnsupportedOperationException("Leaf nodes cannot have children.");
}
}
/**
* Create an outline for a file in the index.
* @param scope the file scope
* @param path the file for which to build the outline
* @return a list of entries constituting the file outline.
* Returns an empty list if the indexer hasn't indexed that path.
*/
public List<Entry> generate(Indexer idx, String abspath) throws Exception {
ModuleType mt = idx.getModuleForFile(abspath);
if (mt == null) {
return new ArrayList<Entry>();
}
return generate(mt.getTable(), abspath);
}
/**
* Create an outline for a symbol table.
* @param scope the file scope
* @param path the file for which we're building the outline
* @return a list of entries constituting the outline
*/
public List<Entry> generate(Scope scope, String path) {
List<Entry> result = new ArrayList<Entry>();
Set<Binding> entries = new TreeSet<Binding>();
for (Binding nb : scope.values()) {
if (!nb.isSynthetic()
&& !nb.isBuiltin()
&& !nb.getDefs().isEmpty()
&& path.equals(nb.getSignatureNode().getFile())) {
entries.add(nb);
}
}
for (Binding nb : entries) {
Def signode = nb.getSignatureNode();
List<Entry> kids = null;
if (nb.getKind() == Binding.Kind.CLASS) {
Type realType = nb.getType();
if (realType.isUnionType()) {
for (Type t : realType.asUnionType().getTypes()) {
if (t.isClassType()) {
realType = t;
break;
}
}
}
kids = generate(realType.getTable(), path);
}
Entry kid = kids != null ? new Branch() : new Leaf();
kid.setOffset(signode.getStart());
kid.setQname(nb.getQname());
kid.setKind(nb.getKind());
if (kids != null) {
kid.setChildren(kids);
}
result.add(kid);
}
return result;
}
}