@adhisang/minecraft-modding-mcp 1.1.0 → 1.1.1
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/CHANGELOG.md +9 -0
- package/README.md +5 -1
- package/dist/cli.js +1 -16
- package/dist/compat-stdio-transport.d.ts +27 -0
- package/dist/compat-stdio-transport.js +217 -0
- package/dist/index.js +2 -2
- package/dist/source-service.js +58 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,11 +5,20 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.1] - 2026-03-02
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `search-class-source` now safely supports recursive `fileGlob` patterns such as `**` without regex construction failures.
|
|
12
|
+
- `get-class-source` now rejects package-incompatible fallback matches and preserves canonical inner-class (`Outer.Inner`) lookup support.
|
|
13
|
+
|
|
8
14
|
## [1.1.0] - 2026-03-01
|
|
9
15
|
|
|
10
16
|
### Changed
|
|
11
17
|
- Migrate stdio transport from mcp-use to @modelcontextprotocol/sdk.
|
|
12
18
|
|
|
19
|
+
### Fixed
|
|
20
|
+
- Restore Codex startup handshake compatibility by accepting both newline-delimited and `Content-Length` stdio framing.
|
|
21
|
+
|
|
13
22
|
### Documentation
|
|
14
23
|
- Add Quick Start setup for Claude Code, OpenAI Codex CLI, and Gemini CLI.
|
|
15
24
|
|
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ codex mcp add minecraft-modding -- npx -y @adhisang/minecraft-modding-mcp
|
|
|
55
55
|
codex mcp list
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
The stdio transport auto-detects both newline-delimited and `Content-Length` framing, so Codex and newline-based MCP clients can use the same server command.
|
|
59
|
+
|
|
58
60
|
#### Gemini CLI
|
|
59
61
|
|
|
60
62
|
Add the following to `~/.gemini/settings.json`:
|
|
@@ -250,6 +252,8 @@ Tools for querying generated registry data and inspecting server runtime state.
|
|
|
250
252
|
`get-class-members` requires either `artifactId` or `targetKind`+`targetValue`, and needs a binary jar (`binaryJarPath`) to read `.class` entries.
|
|
251
253
|
`search-class-source` uses `limit: 20` by default; `snippetLines` defaults to `8` and is clamped to `1..80`; `includeDefinition` and `includeOneHop` default to `false`.
|
|
252
254
|
`search-class-source` with `match=regex` enforces `query.length <= 200` and a strict result cap of `100`.
|
|
255
|
+
`search-class-source` `fileGlob` supports `*`, `**`, and `?`; recursive patterns such as `net/minecraft/**/*.java` are supported.
|
|
256
|
+
`get-class-source` fallback matching enforces package compatibility and returns `ERR_CLASS_NOT_FOUND` when only name-colliding classes from other packages exist.
|
|
253
257
|
`resolve-artifact` with `targetKind=jar` only auto-adopts the exact sibling `"<jar-basename>-sources.jar"`. Other adjacent `*-sources.jar` files are returned as `adjacentSourceCandidates` info only and are never auto-selected.
|
|
254
258
|
Mod tool `jarPath` inputs are normalized to a canonical local `.jar` file path before existence checks, cache keying, and processing.
|
|
255
259
|
`search-mod-source` enforces `query.length <= 200` and `limit <= 200`.
|
|
@@ -782,7 +786,7 @@ Use `resolve-workspace-symbol` when you need compile-visible names from actual G
|
|
|
782
786
|
| Component | Technology |
|
|
783
787
|
| --- | --- |
|
|
784
788
|
| Runtime | Node.js 22+ (native `node:sqlite`) |
|
|
785
|
-
| Transport | stdio (MCP standard) |
|
|
789
|
+
| Transport | stdio (MCP standard, auto-detects newline + `Content-Length` framing) |
|
|
786
790
|
| Storage | SQLite — artifact metadata, source index, mapping cache |
|
|
787
791
|
| Decompilation | [Vineflower](https://github.com/Vineflower/vineflower) (auto-downloaded) |
|
|
788
792
|
| Remapping | [tiny-remapper](https://github.com/FabricMC/tiny-remapper) (requires Java) |
|
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { startServer } from "./index.js";
|
|
3
|
-
function keepProcessAliveUntilStdinCloses() {
|
|
4
|
-
return new Promise((resolve) => {
|
|
5
|
-
const keepAlive = setInterval(() => { }, 1 << 30);
|
|
6
|
-
const release = () => {
|
|
7
|
-
clearInterval(keepAlive);
|
|
8
|
-
resolve();
|
|
9
|
-
};
|
|
10
|
-
if (process.stdin.destroyed) {
|
|
11
|
-
release();
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
process.stdin.once("end", release);
|
|
15
|
-
process.stdin.once("close", release);
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
3
|
startServer()
|
|
19
|
-
.then(() =>
|
|
4
|
+
.then(() => undefined)
|
|
20
5
|
.catch((err) => {
|
|
21
6
|
console.error("Fatal: server failed to start", err);
|
|
22
7
|
process.exit(1);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
type StdioReadable = NodeJS.ReadStream;
|
|
3
|
+
type StdioWritable = NodeJS.WriteStream;
|
|
4
|
+
export declare class CompatStdioServerTransport {
|
|
5
|
+
private readonly stdin;
|
|
6
|
+
private readonly stdout;
|
|
7
|
+
private started;
|
|
8
|
+
private closed;
|
|
9
|
+
private mode;
|
|
10
|
+
private buffer;
|
|
11
|
+
onclose?: () => void;
|
|
12
|
+
onerror?: (error: Error) => void;
|
|
13
|
+
onmessage?: (message: JSONRPCMessage) => void;
|
|
14
|
+
constructor(stdin?: StdioReadable, stdout?: StdioWritable);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
send(message: JSONRPCMessage): Promise<void>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
private readonly handleData;
|
|
19
|
+
private readonly handleStreamError;
|
|
20
|
+
private readonly handleStreamClosed;
|
|
21
|
+
private emitCloseOnce;
|
|
22
|
+
private processReadBuffer;
|
|
23
|
+
private detectMode;
|
|
24
|
+
private readLineDelimitedMessage;
|
|
25
|
+
private readContentLengthMessage;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
function findHeaderBoundary(buffer) {
|
|
4
|
+
const crlfBoundary = buffer.indexOf("\r\n\r\n");
|
|
5
|
+
if (crlfBoundary !== -1) {
|
|
6
|
+
return { index: crlfBoundary, delimiterBytes: 4 };
|
|
7
|
+
}
|
|
8
|
+
const lfBoundary = buffer.indexOf("\n\n");
|
|
9
|
+
if (lfBoundary !== -1) {
|
|
10
|
+
return { index: lfBoundary, delimiterBytes: 2 };
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
function parseJsonRpcMessage(json) {
|
|
15
|
+
return JSONRPCMessageSchema.parse(JSON.parse(json));
|
|
16
|
+
}
|
|
17
|
+
function asError(value) {
|
|
18
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
19
|
+
}
|
|
20
|
+
export class CompatStdioServerTransport {
|
|
21
|
+
stdin;
|
|
22
|
+
stdout;
|
|
23
|
+
started = false;
|
|
24
|
+
closed = false;
|
|
25
|
+
mode = "unknown";
|
|
26
|
+
buffer = Buffer.alloc(0);
|
|
27
|
+
onclose;
|
|
28
|
+
onerror;
|
|
29
|
+
onmessage;
|
|
30
|
+
constructor(stdin = process.stdin, stdout = process.stdout) {
|
|
31
|
+
this.stdin = stdin;
|
|
32
|
+
this.stdout = stdout;
|
|
33
|
+
}
|
|
34
|
+
async start() {
|
|
35
|
+
if (this.started) {
|
|
36
|
+
throw new Error("CompatStdioServerTransport already started. connect() should be called only once.");
|
|
37
|
+
}
|
|
38
|
+
this.started = true;
|
|
39
|
+
this.stdin.on("data", this.handleData);
|
|
40
|
+
this.stdin.on("error", this.handleStreamError);
|
|
41
|
+
this.stdin.on("end", this.handleStreamClosed);
|
|
42
|
+
this.stdin.on("close", this.handleStreamClosed);
|
|
43
|
+
this.stdin.resume();
|
|
44
|
+
}
|
|
45
|
+
async send(message) {
|
|
46
|
+
const json = JSON.stringify(message);
|
|
47
|
+
const frame = this.mode === "content-length"
|
|
48
|
+
? `Content-Length: ${Buffer.byteLength(json, "utf8")}\r\n\r\n${json}`
|
|
49
|
+
: `${json}\n`;
|
|
50
|
+
await new Promise((resolve) => {
|
|
51
|
+
if (this.stdout.write(frame)) {
|
|
52
|
+
resolve();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.stdout.once("drain", resolve);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async close() {
|
|
59
|
+
this.stdin.off("data", this.handleData);
|
|
60
|
+
this.stdin.off("error", this.handleStreamError);
|
|
61
|
+
this.stdin.off("end", this.handleStreamClosed);
|
|
62
|
+
this.stdin.off("close", this.handleStreamClosed);
|
|
63
|
+
if (this.stdin.listenerCount("data") === 0) {
|
|
64
|
+
this.stdin.pause();
|
|
65
|
+
}
|
|
66
|
+
this.buffer = Buffer.alloc(0);
|
|
67
|
+
this.emitCloseOnce();
|
|
68
|
+
}
|
|
69
|
+
handleData = (chunk) => {
|
|
70
|
+
if (chunk.length === 0) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
74
|
+
this.processReadBuffer();
|
|
75
|
+
};
|
|
76
|
+
handleStreamError = (error) => {
|
|
77
|
+
this.onerror?.(error);
|
|
78
|
+
};
|
|
79
|
+
handleStreamClosed = () => {
|
|
80
|
+
this.emitCloseOnce();
|
|
81
|
+
};
|
|
82
|
+
emitCloseOnce() {
|
|
83
|
+
if (this.closed) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.closed = true;
|
|
87
|
+
this.onclose?.();
|
|
88
|
+
}
|
|
89
|
+
processReadBuffer() {
|
|
90
|
+
while (true) {
|
|
91
|
+
try {
|
|
92
|
+
if (this.mode === "unknown") {
|
|
93
|
+
const detected = this.detectMode();
|
|
94
|
+
if (!detected) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.mode = detected;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const modeBefore = this.mode;
|
|
101
|
+
const message = this.mode === "content-length"
|
|
102
|
+
? this.readContentLengthMessage()
|
|
103
|
+
: this.readLineDelimitedMessage();
|
|
104
|
+
if (!message) {
|
|
105
|
+
// readLineDelimitedMessage may switch mode to "content-length"
|
|
106
|
+
// mid-stream; retry with the new parser instead of stopping.
|
|
107
|
+
if (this.mode !== modeBefore) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.onmessage?.(message);
|
|
113
|
+
}
|
|
114
|
+
catch (caughtError) {
|
|
115
|
+
this.onerror?.(asError(caughtError));
|
|
116
|
+
this.mode = "unknown";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
detectMode() {
|
|
121
|
+
// Skip blank leading lines that some clients may emit.
|
|
122
|
+
while (this.buffer.length > 0) {
|
|
123
|
+
if (this.buffer[0] === 0x0a) {
|
|
124
|
+
this.buffer = this.buffer.subarray(1);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (this.buffer.length >= 2 && this.buffer[0] === 0x0d && this.buffer[1] === 0x0a) {
|
|
128
|
+
this.buffer = this.buffer.subarray(2);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
if (this.buffer.length === 0) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const prefix = this.buffer
|
|
137
|
+
.subarray(0, Math.min(this.buffer.length, 32))
|
|
138
|
+
.toString("utf8")
|
|
139
|
+
.toLowerCase();
|
|
140
|
+
if (prefix.startsWith("content-length")) {
|
|
141
|
+
return "content-length";
|
|
142
|
+
}
|
|
143
|
+
const firstNewline = this.buffer.indexOf(0x0a);
|
|
144
|
+
if (firstNewline === -1) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
const firstLine = this.buffer.subarray(0, firstNewline).toString("utf8").replace(/\r$/, "");
|
|
148
|
+
if (/^\s*content-length\s*:/i.test(firstLine)) {
|
|
149
|
+
return "content-length";
|
|
150
|
+
}
|
|
151
|
+
return "line";
|
|
152
|
+
}
|
|
153
|
+
readLineDelimitedMessage() {
|
|
154
|
+
while (true) {
|
|
155
|
+
const newlineIndex = this.buffer.indexOf(0x0a);
|
|
156
|
+
if (newlineIndex === -1) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
const line = this.buffer.subarray(0, newlineIndex).toString("utf8").replace(/\r$/, "");
|
|
160
|
+
this.buffer = this.buffer.subarray(newlineIndex + 1);
|
|
161
|
+
if (line.trim().length === 0) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (/^\s*content-length\s*:/i.test(line)) {
|
|
165
|
+
// Reconstruct the header with the correct line ending so that
|
|
166
|
+
// findHeaderBoundary can locate \r\n\r\n or \n\n reliably.
|
|
167
|
+
const sep = this.buffer.length > 0 && this.buffer[0] === 0x0d ? "\r\n" : "\n";
|
|
168
|
+
this.buffer = Buffer.concat([Buffer.from(`${line}${sep}`, "utf8"), this.buffer]);
|
|
169
|
+
this.mode = "content-length";
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
return parseJsonRpcMessage(line);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
readContentLengthMessage() {
|
|
176
|
+
const headerBoundary = findHeaderBoundary(this.buffer);
|
|
177
|
+
if (!headerBoundary) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
const headersRaw = this.buffer.subarray(0, headerBoundary.index).toString("utf8");
|
|
181
|
+
const headerLines = headersRaw
|
|
182
|
+
.split(/\r?\n/)
|
|
183
|
+
.map((line) => line.trim())
|
|
184
|
+
.filter((line) => line.length > 0);
|
|
185
|
+
let contentLength;
|
|
186
|
+
for (const headerLine of headerLines) {
|
|
187
|
+
const separatorIndex = headerLine.indexOf(":");
|
|
188
|
+
if (separatorIndex === -1) {
|
|
189
|
+
this.buffer = this.buffer.subarray(headerBoundary.index + headerBoundary.delimiterBytes);
|
|
190
|
+
throw new Error(`Malformed header line: ${headerLine}`);
|
|
191
|
+
}
|
|
192
|
+
const headerName = headerLine.slice(0, separatorIndex).trim().toLowerCase();
|
|
193
|
+
const headerValue = headerLine.slice(separatorIndex + 1).trim();
|
|
194
|
+
if (headerName === "content-length") {
|
|
195
|
+
const parsed = Number.parseInt(headerValue, 10);
|
|
196
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
197
|
+
this.buffer = this.buffer.subarray(headerBoundary.index + headerBoundary.delimiterBytes);
|
|
198
|
+
throw new Error(`Invalid Content-Length header value: ${headerValue}`);
|
|
199
|
+
}
|
|
200
|
+
contentLength = parsed;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (contentLength === undefined) {
|
|
204
|
+
this.buffer = this.buffer.subarray(headerBoundary.index + headerBoundary.delimiterBytes);
|
|
205
|
+
throw new Error("Missing Content-Length header.");
|
|
206
|
+
}
|
|
207
|
+
const messageStart = headerBoundary.index + headerBoundary.delimiterBytes;
|
|
208
|
+
const frameEnd = messageStart + contentLength;
|
|
209
|
+
if (this.buffer.length < frameEnd) {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
const body = this.buffer.subarray(messageStart, frameEnd).toString("utf8");
|
|
213
|
+
this.buffer = this.buffer.subarray(frameEnd);
|
|
214
|
+
return parseJsonRpcMessage(body);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=compat-stdio-transport.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { ZodError, z } from "zod";
|
|
4
|
+
import { CompatStdioServerTransport } from "./compat-stdio-transport.js";
|
|
5
5
|
import { objectResult } from "./mcp-helpers.js";
|
|
6
6
|
import { loadConfig } from "./config.js";
|
|
7
7
|
import { ERROR_CODES, isAppError } from "./errors.js";
|
|
@@ -1034,7 +1034,7 @@ export async function startServer() {
|
|
|
1034
1034
|
sqlitePath: config.sqlitePath,
|
|
1035
1035
|
sourceRepos: config.sourceRepos.length
|
|
1036
1036
|
});
|
|
1037
|
-
const transport = new
|
|
1037
|
+
const transport = new CompatStdioServerTransport();
|
|
1038
1038
|
await server.connect(transport);
|
|
1039
1039
|
// In stdio mode, explicitly resume stdin so JSON-RPC lines are consumed.
|
|
1040
1040
|
process.stdin.resume();
|
package/dist/source-service.js
CHANGED
|
@@ -258,11 +258,33 @@ function canUseIndexedSearchPath(indexedSearchEnabled, intent, match, _scope) {
|
|
|
258
258
|
return true;
|
|
259
259
|
}
|
|
260
260
|
function buildGlobRegex(pattern) {
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
261
|
+
const REGEX_META = /[-/\\^$+.()|[\]{}]/;
|
|
262
|
+
let result = "";
|
|
263
|
+
let i = 0;
|
|
264
|
+
while (i < pattern.length) {
|
|
265
|
+
const ch = pattern[i];
|
|
266
|
+
if (ch === "*" && pattern[i + 1] === "*") {
|
|
267
|
+
result += ".*";
|
|
268
|
+
i += 2;
|
|
269
|
+
if (pattern[i] === "/") {
|
|
270
|
+
result += "(?:/)?";
|
|
271
|
+
i += 1;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else if (ch === "*") {
|
|
275
|
+
result += "[^/]*";
|
|
276
|
+
i += 1;
|
|
277
|
+
}
|
|
278
|
+
else if (ch === "?") {
|
|
279
|
+
result += "[^/]";
|
|
280
|
+
i += 1;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
result += REGEX_META.test(ch) ? `\\${ch}` : ch;
|
|
284
|
+
i += 1;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return new RegExp(`^${result}$`);
|
|
266
288
|
}
|
|
267
289
|
function globToSqlLike(pattern) {
|
|
268
290
|
let result = "";
|
|
@@ -283,6 +305,27 @@ function globToSqlLike(pattern) {
|
|
|
283
305
|
}
|
|
284
306
|
return result;
|
|
285
307
|
}
|
|
308
|
+
function isPackageCompatible(filePath, classPath) {
|
|
309
|
+
const lastSlash = classPath.lastIndexOf("/");
|
|
310
|
+
if (lastSlash < 0)
|
|
311
|
+
return true;
|
|
312
|
+
const expectedPrefix = classPath.slice(0, lastSlash + 1);
|
|
313
|
+
return filePath.startsWith(expectedPrefix);
|
|
314
|
+
}
|
|
315
|
+
function classNameToClassPath(className) {
|
|
316
|
+
const normalized = normalizePathStyle(className.trim()).replace(/\//g, ".");
|
|
317
|
+
const segments = normalized.split(".").filter((segment) => segment.length > 0);
|
|
318
|
+
if (segments.length === 0) {
|
|
319
|
+
return "";
|
|
320
|
+
}
|
|
321
|
+
const firstTypeSegment = segments.findIndex((segment) => /^[A-Z_$]/.test(segment));
|
|
322
|
+
if (firstTypeSegment < 0) {
|
|
323
|
+
return segments.join("/");
|
|
324
|
+
}
|
|
325
|
+
const packagePath = segments.slice(0, firstTypeSegment).join("/");
|
|
326
|
+
const typePath = segments.slice(firstTypeSegment).join("$");
|
|
327
|
+
return packagePath ? `${packagePath}/${typePath}` : typePath;
|
|
328
|
+
}
|
|
286
329
|
function checkPackagePrefix(filePath, packagePrefix) {
|
|
287
330
|
if (!packagePrefix) {
|
|
288
331
|
return true;
|
|
@@ -2303,7 +2346,10 @@ export class SourceService {
|
|
|
2303
2346
|
}
|
|
2304
2347
|
resolveClassFilePath(artifactId, className) {
|
|
2305
2348
|
const normalizedClassName = className.trim();
|
|
2306
|
-
const classPath =
|
|
2349
|
+
const classPath = classNameToClassPath(normalizedClassName);
|
|
2350
|
+
if (!classPath) {
|
|
2351
|
+
return undefined;
|
|
2352
|
+
}
|
|
2307
2353
|
const candidates = new Set([`${classPath}.java`]);
|
|
2308
2354
|
const innerIndex = classPath.indexOf("$");
|
|
2309
2355
|
if (innerIndex > 0) {
|
|
@@ -2320,10 +2366,14 @@ export class SourceService {
|
|
|
2320
2366
|
return undefined;
|
|
2321
2367
|
}
|
|
2322
2368
|
const classPathBySymbol = this.symbolsRepo.findBestClassFilePath(artifactId, normalizedClassName, simpleName);
|
|
2323
|
-
if (classPathBySymbol) {
|
|
2369
|
+
if (classPathBySymbol && isPackageCompatible(classPathBySymbol, classPath)) {
|
|
2324
2370
|
return classPathBySymbol;
|
|
2325
2371
|
}
|
|
2326
|
-
|
|
2372
|
+
const byName = this.filesRepo.findFirstFilePathByName(artifactId, `${simpleName}.java`);
|
|
2373
|
+
if (byName && isPackageCompatible(byName, classPath)) {
|
|
2374
|
+
return byName;
|
|
2375
|
+
}
|
|
2376
|
+
return undefined;
|
|
2327
2377
|
}
|
|
2328
2378
|
findNearestSymbolForLine(artifactId, filePath, line, symbolKind) {
|
|
2329
2379
|
const symbols = this.symbolsRepo
|