@kaluchi/jdtbridge 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaluchi/jdtbridge",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "CLI for Eclipse JDT Bridge — semantic Java analysis via Eclipse JDT SearchEngine",
5
5
  "type": "module",
6
6
  "bin": {
package/src/args.mjs CHANGED
@@ -103,5 +103,17 @@ function splitParams(str) {
103
103
  }
104
104
  const last = str.substring(start).trim();
105
105
  if (last) params.push(last);
106
- return params;
106
+ return params.map(eraseGenerics);
107
+ }
108
+
109
+ /** Strip generics: Map<String,Integer> → Map, List<String>[] → List[] */
110
+ function eraseGenerics(type) {
111
+ let result = "";
112
+ let depth = 0;
113
+ for (const ch of type) {
114
+ if (ch === "<") depth++;
115
+ else if (ch === ">") depth--;
116
+ else if (depth === 0) result += ch;
117
+ }
118
+ return result;
107
119
  }
@@ -1,7 +1,6 @@
1
1
  import { get } from "../client.mjs";
2
2
  import { extractPositional } from "../args.mjs";
3
- import { stripProject } from "../paths.mjs";
4
- import { bold } from "../color.mjs";
3
+ import { formatHierarchy } from "../format/hierarchy.mjs";
5
4
 
6
5
  export async function hierarchy(args) {
7
6
  const pos = extractPositional(args);
@@ -18,26 +17,16 @@ export async function hierarchy(args) {
18
17
  console.error(result.error);
19
18
  process.exit(1);
20
19
  }
21
- if (result.supers.length > 0) {
22
- console.log(bold("Superclasses:"));
23
- for (const s of result.supers) {
24
- const loc = s.binary ? "(binary)" : stripProject(s.file);
25
- console.log(` ${s.fqn} ${loc}`);
26
- }
27
- }
28
- if (result.interfaces.length > 0) {
29
- console.log(bold("Interfaces:"));
30
- for (const s of result.interfaces) {
31
- const loc = s.binary ? "(binary)" : stripProject(s.file);
32
- console.log(` ${s.fqn} ${loc}`);
33
- }
34
- }
35
- if (result.subtypes.length > 0) {
36
- console.log(bold("Subtypes:"));
37
- for (const s of result.subtypes) {
38
- const loc = s.binary ? "(binary)" : stripProject(s.file);
39
- console.log(` ${s.fqn} ${loc}`);
40
- }
20
+
21
+ const lines = [];
22
+ lines.push(`#### ${result.fqn || fqn}`);
23
+ lines.push("");
24
+ lines.push(...formatHierarchy(result));
25
+
26
+ if (lines.length > 2) {
27
+ console.log(lines.join("\n"));
28
+ } else {
29
+ console.log("No hierarchy found.");
41
30
  }
42
31
  }
43
32
 
@@ -1,5 +1,6 @@
1
1
  import { get } from "../client.mjs";
2
2
  import { extractPositional, parseFqmn } from "../args.mjs";
3
+ import { formatHierEntry } from "../format/hierarchy.mjs";
3
4
 
4
5
  export async function source(args) {
5
6
  const jsonFlag = args.includes("--json");
@@ -91,8 +92,8 @@ function formatMemberRef(ref) {
91
92
  if (ref.inherited) annotations.push("inherited");
92
93
  if (annotations.length > 0) line += ` (${annotations.join(", ")})`;
93
94
 
94
- // Line number (only for incoming)
95
- if (ref.direction === "incoming" && ref.line) line += ` :${ref.line}`;
95
+ // Line number: server sends it but we don't render for incoming
96
+ // callers are navigable by FQMN, line numbers just add noise
96
97
 
97
98
  // Javadoc inline after —
98
99
  if (ref.doc) line += ` — ${ref.doc}`;
@@ -126,19 +127,31 @@ function groupByDeclaringType(refs) {
126
127
  return groups;
127
128
  }
128
129
 
129
- function formatRefGroup({ typeFqn, group }, implIndex) {
130
+ function formatRefGroup({ typeFqn, group }, implIndex, viewScope) {
130
131
  const lines = [];
131
- if (group.typeRef) {
132
+ // Type header: only show for standalone type refs (no members).
133
+ // When members exist, the type is already visible in their FQMNs.
134
+ const standalone = group.members.length === 0;
135
+ if (standalone && group.typeRef) {
132
136
  lines.push(formatTypeHeader(group.typeRef));
133
- } else if (group.members.length > 0) {
134
- const tkBadge = TYPE_KIND_BADGE[group.members[0].typeKind] || "[C]";
135
- lines.push(`${tkBadge} \`${typeFqn}\``);
137
+ // Show type implementations (domain-scoped)
138
+ const impls = implIndex[group.typeRef.fqmn];
139
+ if (impls && !(viewScope === "project" && group.typeRef.scope === "dependency")) {
140
+ for (const impl of impls) {
141
+ let implLine = ` ${badge(impl)} \`${impl.fqmn}\``;
142
+ if (impl.anonymous && impl.enclosingFqmn) {
143
+ implLine += ` — in \`${impl.enclosingFqmn}\``;
144
+ }
145
+ lines.push(implLine);
146
+ }
147
+ }
136
148
  }
137
149
  for (const ref of group.members) {
138
150
  lines.push(formatMemberRef(ref));
139
- // Show implementations right after the interface method
151
+ // Show implementations skip dependency interface impls
152
+ // when viewing project source (domain scoping)
140
153
  const impls = implIndex[ref.fqmn];
141
- if (impls) {
154
+ if (impls && !(viewScope === "project" && ref.scope === "dependency")) {
142
155
  for (const impl of impls) {
143
156
  lines.push(` → ${badge(impl)} \`${impl.fqmn}\``);
144
157
  }
@@ -159,21 +172,20 @@ function buildImplIndex(refs) {
159
172
  return index;
160
173
  }
161
174
 
175
+
162
176
  // ---- Hierarchy (type-level) ----
163
177
 
164
- function formatHierarchy(lines, result) {
178
+ function formatHierarchySection(lines, result) {
165
179
  const supers = result.supertypes || [];
166
180
  const subs = result.subtypes || [];
167
181
  if (supers.length > 0 || subs.length > 0) {
168
182
  lines.push("");
169
183
  lines.push("#### Hierarchy:");
170
184
  for (const s of supers) {
171
- const b = TYPE_KIND_BADGE[s.kind] || "[C]";
172
- lines.push(`↑ ${b} \`${s.fqn}\``);
185
+ lines.push(...formatHierEntry("", s));
173
186
  }
174
187
  for (const s of subs) {
175
- const b = TYPE_KIND_BADGE[s.kind] || "[C]";
176
- lines.push(`↓ ${b} \`${s.fqn}\``);
188
+ lines.push(...formatHierEntry("", s));
177
189
  }
178
190
  }
179
191
  if (result.enclosingType) {
@@ -182,7 +194,7 @@ function formatHierarchy(lines, result) {
182
194
  const et = result.enclosingType;
183
195
  const fqn = typeof et === "string" ? et : et.fqn;
184
196
  const kind = typeof et === "string" ? "class" : (et.kind || "class");
185
- lines.push(`${TYPE_KIND_BADGE[kind] || "[C]"} \`${fqn}\``);
197
+ lines.push(...formatHierEntry("", { fqn, kind, ...et }));
186
198
  }
187
199
  }
188
200
 
@@ -212,7 +224,7 @@ function formatMarkdown(result) {
212
224
 
213
225
  // Type-level: hierarchy instead of refs
214
226
  if (result.supertypes || result.subtypes) {
215
- formatHierarchy(lines, result);
227
+ formatHierarchySection(lines, result);
216
228
  return lines.join("\n");
217
229
  }
218
230
 
@@ -220,10 +232,32 @@ function formatMarkdown(result) {
220
232
  return lines.join("\n");
221
233
  }
222
234
 
223
- // Split by direction
224
- const outgoing = result.refs.filter((r) => r.direction !== "incoming");
235
+ // Self-reference: the viewed member's declaring type
236
+ const selfFqn = result.fqmn.includes("#")
237
+ ? result.fqmn.split("#")[0] : null;
238
+
239
+ // Split by direction, filter self-reference type refs
240
+ const outgoing = result.refs.filter((r) =>
241
+ r.direction !== "incoming"
242
+ && !(r.kind === "type" && r.fqmn === selfFqn));
225
243
  const incoming = result.refs.filter((r) => r.direction === "incoming");
226
244
 
245
+ const viewScope = result.viewScope;
246
+
247
+ // Implementations section (interface/abstract methods)
248
+ const impls = result.implementations || [];
249
+ if (impls.length > 0) {
250
+ lines.push("");
251
+ lines.push("#### Implementations:");
252
+ for (const impl of impls) {
253
+ let line = `[M] \`${impl.fqmn}\``;
254
+ if (impl.anonymous && impl.enclosingFqmn) {
255
+ line += ` — in \`${impl.enclosingFqmn}\``;
256
+ }
257
+ lines.push(line);
258
+ }
259
+ }
260
+
227
261
  if (outgoing.length > 0) {
228
262
  const implIndex = buildImplIndex(outgoing);
229
263
  const mainRefs = outgoing.filter((r) => !r.implementationOf);
@@ -231,7 +265,7 @@ function formatMarkdown(result) {
231
265
  lines.push("#### Outgoing Calls:");
232
266
  const groups = groupByDeclaringType(mainRefs);
233
267
  for (const g of groups) {
234
- lines.push(formatRefGroup(g, implIndex));
268
+ lines.push(formatRefGroup(g, implIndex, viewScope));
235
269
  }
236
270
  }
237
271
 
@@ -242,7 +276,7 @@ function formatMarkdown(result) {
242
276
  lines.push("#### Incoming Calls:");
243
277
  const groups = groupByDeclaringType(mainRefs);
244
278
  for (const g of groups) {
245
- lines.push(formatRefGroup(g, implIndex));
279
+ lines.push(formatRefGroup(g, implIndex, viewScope));
246
280
  }
247
281
  }
248
282
 
@@ -0,0 +1,47 @@
1
+ const TYPE_KIND_BADGE = {
2
+ class: "[C]",
3
+ interface: "[I]",
4
+ enum: "[E]",
5
+ annotation: "[A]",
6
+ };
7
+
8
+ export function formatHierEntry(arrow, s) {
9
+ const depth = s.depth || 0;
10
+ const indent = " ".repeat(depth);
11
+ const b = TYPE_KIND_BADGE[s.kind] || "[C]";
12
+ const prefix = arrow ? `${arrow} ` : "";
13
+ let line = `${indent}- ${prefix}${b} \`${s.fqn}\``;
14
+ if (s.anonymous && s.enclosingFqmn) {
15
+ line += ` — in \`${s.enclosingFqmn}\``;
16
+ }
17
+ const lines = [line];
18
+ if (s.file) {
19
+ let loc = s.file;
20
+ if (s.line) {
21
+ loc += `:${s.line}`;
22
+ if (s.endLine && s.endLine !== s.line) loc += `-${s.endLine}`;
23
+ }
24
+ lines.push(`${indent} \`${loc}\``);
25
+ }
26
+ return lines;
27
+ }
28
+
29
+ export function formatHierarchy(result) {
30
+ const lines = [];
31
+ const supers = result.supertypes || [];
32
+ const subs = result.subtypes || [];
33
+ if (supers.length > 0) {
34
+ lines.push("#### Supertypes:");
35
+ for (const s of supers) {
36
+ lines.push(...formatHierEntry("↑", s));
37
+ }
38
+ }
39
+ if (subs.length > 0) {
40
+ if (lines.length > 0) lines.push("");
41
+ lines.push("#### Subtypes:");
42
+ for (const s of subs) {
43
+ lines.push(...formatHierEntry("", s));
44
+ }
45
+ }
46
+ return lines;
47
+ }