gdharness 1.1.34 → 1.1.35
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/build/cli.js +76 -31
- package/build/index.js +75 -27
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -17758,7 +17758,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17758
17758
|
},
|
|
17759
17759
|
{
|
|
17760
17760
|
name: "script_edit",
|
|
17761
|
-
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a find_children
|
|
17761
|
+
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment or a line of a text file with nothing more to say is given under leftAloneInProse by file and line instead. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
|
|
17762
17762
|
parameters: {
|
|
17763
17763
|
projectPath: PROJECT_PATH,
|
|
17764
17764
|
scriptPath: SCRIPT_PATH,
|
|
@@ -38854,9 +38854,11 @@ class GodotLSPClient {
|
|
|
38854
38854
|
diagnosticsWaiters = new Map;
|
|
38855
38855
|
documentVersions = new Map;
|
|
38856
38856
|
documentTurns = new Map;
|
|
38857
|
-
|
|
38857
|
+
timeouts;
|
|
38858
|
+
constructor(port = portFromEnv("GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT), host = "127.0.0.1", timeouts = { requestMs: REQUEST_TIMEOUT_MS2, referencesMs: REFERENCES_TIMEOUT_MS }) {
|
|
38858
38859
|
this.port = port;
|
|
38859
38860
|
this.host = host;
|
|
38861
|
+
this.timeouts = timeouts;
|
|
38860
38862
|
this.pendingRequests = new Map;
|
|
38861
38863
|
}
|
|
38862
38864
|
async connect() {
|
|
@@ -38953,7 +38955,7 @@ class GodotLSPClient {
|
|
|
38953
38955
|
const rootPath = this.rootPath ?? dirname13(resolve5(filePath));
|
|
38954
38956
|
await this.initialize(rootPath);
|
|
38955
38957
|
}
|
|
38956
|
-
async sendRequest(method, params) {
|
|
38958
|
+
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
38957
38959
|
await this.ensureConnected();
|
|
38958
38960
|
if (!this.socket) {
|
|
38959
38961
|
throw new Refusal("Not connected to Godot LSP");
|
|
@@ -38970,8 +38972,8 @@ class GodotLSPClient {
|
|
|
38970
38972
|
return new Promise((resolveRequest, rejectRequest) => {
|
|
38971
38973
|
const timer = setTimeout(() => {
|
|
38972
38974
|
this.pendingRequests.delete(id);
|
|
38973
|
-
rejectRequest(new
|
|
38974
|
-
},
|
|
38975
|
+
rejectRequest(new LSPTimeout(method, timeoutMs));
|
|
38976
|
+
}, timeoutMs);
|
|
38975
38977
|
this.pendingRequests.set(id, {
|
|
38976
38978
|
resolve: resolveRequest,
|
|
38977
38979
|
reject: rejectRequest,
|
|
@@ -39276,7 +39278,7 @@ class GodotLSPClient {
|
|
|
39276
39278
|
textDocument: { uri },
|
|
39277
39279
|
position: { line, character },
|
|
39278
39280
|
context: { includeDeclaration: true }
|
|
39279
|
-
});
|
|
39281
|
+
}, this.timeouts.referencesMs);
|
|
39280
39282
|
} finally {
|
|
39281
39283
|
this.closeDocument(uri);
|
|
39282
39284
|
}
|
|
@@ -39434,12 +39436,21 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
39434
39436
|
});
|
|
39435
39437
|
}
|
|
39436
39438
|
}
|
|
39437
|
-
var DEFAULT_LSP_PORT = 6005, DIAGNOSTICS_TIMEOUT_MS = 5000, ArgumentRefusal, AnotherProjectsServer;
|
|
39439
|
+
var DEFAULT_LSP_PORT = 6005, DIAGNOSTICS_TIMEOUT_MS = 5000, REQUEST_TIMEOUT_MS2 = 1e4, REFERENCES_TIMEOUT_MS = 180000, LSPTimeout, ArgumentRefusal, AnotherProjectsServer;
|
|
39438
39440
|
var init_lsp_client = __esm(() => {
|
|
39439
39441
|
init_errors();
|
|
39440
39442
|
init_framing();
|
|
39441
39443
|
init_paths();
|
|
39442
39444
|
init_ports();
|
|
39445
|
+
LSPTimeout = class LSPTimeout extends Error {
|
|
39446
|
+
method;
|
|
39447
|
+
seconds;
|
|
39448
|
+
constructor(method, timeoutMs) {
|
|
39449
|
+
super(`LSP request timed out after ${String(timeoutMs / 1000)}s: ${method}`);
|
|
39450
|
+
this.method = method;
|
|
39451
|
+
this.seconds = timeoutMs / 1000;
|
|
39452
|
+
}
|
|
39453
|
+
};
|
|
39443
39454
|
ArgumentRefusal = class ArgumentRefusal extends Refusal {
|
|
39444
39455
|
};
|
|
39445
39456
|
AnotherProjectsServer = class AnotherProjectsServer extends Refusal {
|
|
@@ -40061,6 +40072,10 @@ class PlanBuilder {
|
|
|
40061
40072
|
return { edits, mentions };
|
|
40062
40073
|
}
|
|
40063
40074
|
}
|
|
40075
|
+
function isWholeString(text, offset, name) {
|
|
40076
|
+
const before = text[offset - 1];
|
|
40077
|
+
return (before === '"' || before === "'") && text[offset + name.length] === before;
|
|
40078
|
+
}
|
|
40064
40079
|
function declaresItsOwn(script, name, except) {
|
|
40065
40080
|
return script.occurrences(name).some((occurrence) => occurrence.kind === "code" && occurrence.offset !== except && occurrence.declaredAs !== null && occurrence.declaredAs !== "class_name");
|
|
40066
40081
|
}
|
|
@@ -40101,6 +40116,9 @@ function planClassRename(args) {
|
|
|
40101
40116
|
}
|
|
40102
40117
|
plan.mentionTheRest(texts, scripts, (file, occurrence, kind) => {
|
|
40103
40118
|
const script = scripts.get(file);
|
|
40119
|
+
if (kind === "string" && script !== undefined && isWholeString(script.text, occurrence.offset, oldName)) {
|
|
40120
|
+
return WHOLE_CLASS_NAME;
|
|
40121
|
+
}
|
|
40104
40122
|
if (kind !== "code" || script === undefined) {
|
|
40105
40123
|
return;
|
|
40106
40124
|
}
|
|
@@ -40167,7 +40185,16 @@ function planMemberRename(args) {
|
|
|
40167
40185
|
}
|
|
40168
40186
|
}
|
|
40169
40187
|
planScenes(texts, heirs, kind, oldName, plan);
|
|
40170
|
-
plan.mentionTheRest(texts, scripts, (
|
|
40188
|
+
plan.mentionTheRest(texts, scripts, (file, occurrence, mentionKind) => {
|
|
40189
|
+
const script = scripts.get(file);
|
|
40190
|
+
if (mentionKind === "string" && script !== undefined && isWholeString(script.text, occurrence.offset, oldName)) {
|
|
40191
|
+
return WHOLE_MEMBER_NAME;
|
|
40192
|
+
}
|
|
40193
|
+
if (mentionKind !== "code") {
|
|
40194
|
+
return;
|
|
40195
|
+
}
|
|
40196
|
+
return occurrence.afterDot ? "not resolved to this member by the language server: another class with a member of the same name, or a value whose type the analyser could not tell" : "not resolved to this member by the language server";
|
|
40197
|
+
});
|
|
40171
40198
|
return plan.build();
|
|
40172
40199
|
}
|
|
40173
40200
|
function sectionsOf(text) {
|
|
@@ -40503,7 +40530,7 @@ function applyRewrites(rewrites) {
|
|
|
40503
40530
|
throw error;
|
|
40504
40531
|
}
|
|
40505
40532
|
}
|
|
40506
|
-
var DOC_LINK_KINDS, DOC_KIND;
|
|
40533
|
+
var DOC_LINK_KINDS, DOC_KIND, WHOLE_CLASS_NAME = "the whole string is the class's name, as find_children's type, a comparison with get_global_name() or a list of accepted type names takes it, and it was not changed: if it names this class, it no longer matches anything after the rename, and no compile says so", WHOLE_MEMBER_NAME = "the whole string is the member's name, as call, connect, has_method, get or set take it, and it was not changed: if it names this member, it no longer finds it after the rename, and no compile says so";
|
|
40507
40534
|
var init_rename = __esm(() => {
|
|
40508
40535
|
init_gdscript_source();
|
|
40509
40536
|
DOC_LINK_KINDS = new Set([
|
|
@@ -40612,6 +40639,7 @@ async function renameSymbol(request, services) {
|
|
|
40612
40639
|
let plan;
|
|
40613
40640
|
let what;
|
|
40614
40641
|
let resolvedBy;
|
|
40642
|
+
let took = null;
|
|
40615
40643
|
let overrides = [];
|
|
40616
40644
|
let declaringFiles = [declaringPath];
|
|
40617
40645
|
if (isClass) {
|
|
@@ -40718,7 +40746,23 @@ async function renameSymbol(request, services) {
|
|
|
40718
40746
|
}
|
|
40719
40747
|
overrides = family.map((one) => one.file).filter((file) => file !== fileOf(root));
|
|
40720
40748
|
declaringFiles = family.map((one) => one.file);
|
|
40721
|
-
|
|
40749
|
+
took = { references: { asked: 0, ms: 0 }, definitions: { asked: 0, ms: 0 } };
|
|
40750
|
+
let resolved;
|
|
40751
|
+
try {
|
|
40752
|
+
resolved = await resolveMember(projectPath, texts, scripts, family, symbol, services, took);
|
|
40753
|
+
} catch (error) {
|
|
40754
|
+
if (!(error instanceof LSPTimeout)) {
|
|
40755
|
+
throw error;
|
|
40756
|
+
}
|
|
40757
|
+
return {
|
|
40758
|
+
ok: false,
|
|
40759
|
+
reason: `The editor's language server took the ${error.method} request for ${symbol} and had not answered it after ${String(error.seconds)}s, so nothing was renamed.${error.method === "textDocument/references" ? " Godot answers a references request by looking for the name's text in every script and resolving each match, so a name also written as a common local takes longest." : ""}`,
|
|
40760
|
+
advice: [
|
|
40761
|
+
"script_diagnostics on the declaring script says whether the language server is answering other requests",
|
|
40762
|
+
"The request that ran out is still being answered by the editor, and another sent now waits behind it"
|
|
40763
|
+
]
|
|
40764
|
+
};
|
|
40765
|
+
}
|
|
40722
40766
|
if (resolved === null) {
|
|
40723
40767
|
const first = family[0];
|
|
40724
40768
|
const script = first === undefined ? undefined : scripts.get(first.file);
|
|
@@ -40766,6 +40810,7 @@ async function renameSymbol(request, services) {
|
|
|
40766
40810
|
rewrites,
|
|
40767
40811
|
report: {
|
|
40768
40812
|
renamed: { what, from: symbol, to: newName, declaredIn: declaringPath, resolvedBy },
|
|
40813
|
+
...took === null ? {} : { languageServer: took },
|
|
40769
40814
|
...overrides.length > 0 ? { overridesRenamed: overrides } : {},
|
|
40770
40815
|
...moveTo === null ? {} : { moved: { from: declaringPath, to: moveTo } },
|
|
40771
40816
|
changed: changedReport(plan.edits, rewrites),
|
|
@@ -40774,7 +40819,7 @@ async function renameSymbol(request, services) {
|
|
|
40774
40819
|
}
|
|
40775
40820
|
};
|
|
40776
40821
|
}
|
|
40777
|
-
async function resolveMember(projectPath, texts, scripts, family, name, services) {
|
|
40822
|
+
async function resolveMember(projectPath, texts, scripts, family, name, services, took) {
|
|
40778
40823
|
const absoluteOf = new Map(texts.map((file) => [file.path, file.absolute]));
|
|
40779
40824
|
const key = (place) => `${place.file}:${place.offset}`;
|
|
40780
40825
|
const toResolved = (located) => {
|
|
@@ -40788,11 +40833,16 @@ async function resolveMember(projectPath, texts, scripts, family, name, services
|
|
|
40788
40833
|
if (script === undefined || absolute === undefined) {
|
|
40789
40834
|
return [];
|
|
40790
40835
|
}
|
|
40791
|
-
const
|
|
40792
|
-
|
|
40836
|
+
const started = performance.now();
|
|
40837
|
+
try {
|
|
40838
|
+
const answered = await services[question](absolute, script.text, script.lines.positionOf(place.offset));
|
|
40839
|
+
return answered.map(toResolved).filter((one) => one !== null);
|
|
40840
|
+
} finally {
|
|
40841
|
+
took[question].asked += 1;
|
|
40842
|
+
took[question].ms += Math.round(performance.now() - started);
|
|
40843
|
+
}
|
|
40793
40844
|
};
|
|
40794
40845
|
const ours = new Map;
|
|
40795
|
-
const settled = new Set;
|
|
40796
40846
|
const root = family[0];
|
|
40797
40847
|
if (root === undefined) {
|
|
40798
40848
|
return [];
|
|
@@ -40808,23 +40858,13 @@ async function resolveMember(projectPath, texts, scripts, family, name, services
|
|
|
40808
40858
|
for (const script of scripts.values()) {
|
|
40809
40859
|
for (const occurrence of script.occurrences(name)) {
|
|
40810
40860
|
const place = { file: script.path, offset: occurrence.offset };
|
|
40811
|
-
if (occurrence.kind !== "code" || ours.has(key(place))
|
|
40861
|
+
if (occurrence.kind !== "code" || ours.has(key(place))) {
|
|
40812
40862
|
continue;
|
|
40813
40863
|
}
|
|
40814
40864
|
const declared = await ask("definitions", place);
|
|
40815
40865
|
const only = declared.length === 1 ? declared[0] : undefined;
|
|
40816
|
-
if (only
|
|
40817
|
-
|
|
40818
|
-
continue;
|
|
40819
|
-
}
|
|
40820
|
-
const isOurs = declarations.has(key(only));
|
|
40821
|
-
const same = [place, ...await ask("references", place)];
|
|
40822
|
-
for (const one of same) {
|
|
40823
|
-
if (isOurs) {
|
|
40824
|
-
ours.set(key(one), one);
|
|
40825
|
-
} else {
|
|
40826
|
-
settled.add(key(one));
|
|
40827
|
-
}
|
|
40866
|
+
if (only !== undefined && declarations.has(key(only))) {
|
|
40867
|
+
ours.set(key(place), place);
|
|
40828
40868
|
}
|
|
40829
40869
|
}
|
|
40830
40870
|
}
|
|
@@ -40880,8 +40920,9 @@ ${mention.kind}`;
|
|
|
40880
40920
|
full.push(mention);
|
|
40881
40921
|
}
|
|
40882
40922
|
}
|
|
40923
|
+
const loudFirst = (mention) => mention.kind === "string" && mention.why !== undefined ? 0 : 1;
|
|
40883
40924
|
return {
|
|
40884
|
-
leftAlone: full,
|
|
40925
|
+
leftAlone: full.sort((a, b) => loudFirst(a) - loudFirst(b)),
|
|
40885
40926
|
...prose.size > 0 ? { leftAloneInProse: [...prose.values()] } : {}
|
|
40886
40927
|
};
|
|
40887
40928
|
}
|
|
@@ -40904,6 +40945,7 @@ function summaryOf(edits, mentions, rewrites) {
|
|
|
40904
40945
|
var WHAT;
|
|
40905
40946
|
var init_rename_symbol = __esm(() => {
|
|
40906
40947
|
init_gdscript_source();
|
|
40948
|
+
init_lsp_client();
|
|
40907
40949
|
init_project_scan();
|
|
40908
40950
|
init_rename();
|
|
40909
40951
|
init_resources();
|
|
@@ -45981,7 +46023,9 @@ class GodotServer {
|
|
|
45981
46023
|
}
|
|
45982
46024
|
return await question(client);
|
|
45983
46025
|
} catch (error) {
|
|
45984
|
-
|
|
46026
|
+
if (!(error instanceof LSPTimeout)) {
|
|
46027
|
+
languageServer.problem = normalizeLSPError(error, client.port);
|
|
46028
|
+
}
|
|
45985
46029
|
throw error;
|
|
45986
46030
|
}
|
|
45987
46031
|
};
|
|
@@ -47743,8 +47787,9 @@ that a setting nobody wrote down still answers with the default the engine regis
|
|
|
47743
47787
|
|
|
47744
47788
|
To rename a class or a member across the project, use \`script_edit rename\` rather than a text
|
|
47745
47789
|
replacement: it changes the uses the engine resolves, doc links and what scenes record, and lists
|
|
47746
|
-
every other occurrence of the word for you to read: a
|
|
47747
|
-
|
|
47790
|
+
every other occurrence of the word for you to read: a string that is the name whole, such as a
|
|
47791
|
+
\`find_children\` type, first under \`leftAlone\` since nothing compiles it, a file under a
|
|
47792
|
+
\`.gdignore\` directory there too, and a word in a comment or a text file by line under
|
|
47748
47793
|
\`leftAloneInProse\`. Pass \`preview\` first on a large rename. If the answer has \`ok: false\`
|
|
47749
47794
|
beside \`written: true\`, the files were changed and a script did not compile afterwards.
|
|
47750
47795
|
|
package/build/index.js
CHANGED
|
@@ -29540,6 +29540,18 @@ import { dirname as dirname8, join as join14, resolve as resolve3 } from "node:p
|
|
|
29540
29540
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
29541
29541
|
var DEFAULT_LSP_PORT = 6005;
|
|
29542
29542
|
var DIAGNOSTICS_TIMEOUT_MS = 5000;
|
|
29543
|
+
var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
29544
|
+
var REFERENCES_TIMEOUT_MS = 180000;
|
|
29545
|
+
|
|
29546
|
+
class LSPTimeout extends Error {
|
|
29547
|
+
method;
|
|
29548
|
+
seconds;
|
|
29549
|
+
constructor(method, timeoutMs) {
|
|
29550
|
+
super(`LSP request timed out after ${String(timeoutMs / 1000)}s: ${method}`);
|
|
29551
|
+
this.method = method;
|
|
29552
|
+
this.seconds = timeoutMs / 1000;
|
|
29553
|
+
}
|
|
29554
|
+
}
|
|
29543
29555
|
|
|
29544
29556
|
class ArgumentRefusal extends Refusal {
|
|
29545
29557
|
}
|
|
@@ -29583,9 +29595,11 @@ class GodotLSPClient {
|
|
|
29583
29595
|
diagnosticsWaiters = new Map;
|
|
29584
29596
|
documentVersions = new Map;
|
|
29585
29597
|
documentTurns = new Map;
|
|
29586
|
-
|
|
29598
|
+
timeouts;
|
|
29599
|
+
constructor(port = portFromEnv("GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT), host = "127.0.0.1", timeouts = { requestMs: REQUEST_TIMEOUT_MS2, referencesMs: REFERENCES_TIMEOUT_MS }) {
|
|
29587
29600
|
this.port = port;
|
|
29588
29601
|
this.host = host;
|
|
29602
|
+
this.timeouts = timeouts;
|
|
29589
29603
|
this.pendingRequests = new Map;
|
|
29590
29604
|
}
|
|
29591
29605
|
async connect() {
|
|
@@ -29682,7 +29696,7 @@ class GodotLSPClient {
|
|
|
29682
29696
|
const rootPath = this.rootPath ?? dirname8(resolve3(filePath));
|
|
29683
29697
|
await this.initialize(rootPath);
|
|
29684
29698
|
}
|
|
29685
|
-
async sendRequest(method, params) {
|
|
29699
|
+
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
29686
29700
|
await this.ensureConnected();
|
|
29687
29701
|
if (!this.socket) {
|
|
29688
29702
|
throw new Refusal("Not connected to Godot LSP");
|
|
@@ -29699,8 +29713,8 @@ class GodotLSPClient {
|
|
|
29699
29713
|
return new Promise((resolveRequest, rejectRequest) => {
|
|
29700
29714
|
const timer = setTimeout(() => {
|
|
29701
29715
|
this.pendingRequests.delete(id);
|
|
29702
|
-
rejectRequest(new
|
|
29703
|
-
},
|
|
29716
|
+
rejectRequest(new LSPTimeout(method, timeoutMs));
|
|
29717
|
+
}, timeoutMs);
|
|
29704
29718
|
this.pendingRequests.set(id, {
|
|
29705
29719
|
resolve: resolveRequest,
|
|
29706
29720
|
reject: rejectRequest,
|
|
@@ -30005,7 +30019,7 @@ class GodotLSPClient {
|
|
|
30005
30019
|
textDocument: { uri },
|
|
30006
30020
|
position: { line, character },
|
|
30007
30021
|
context: { includeDeclaration: true }
|
|
30008
|
-
});
|
|
30022
|
+
}, this.timeouts.referencesMs);
|
|
30009
30023
|
} finally {
|
|
30010
30024
|
this.closeDocument(uri);
|
|
30011
30025
|
}
|
|
@@ -30782,6 +30796,12 @@ class PlanBuilder {
|
|
|
30782
30796
|
return { edits, mentions };
|
|
30783
30797
|
}
|
|
30784
30798
|
}
|
|
30799
|
+
function isWholeString(text, offset, name) {
|
|
30800
|
+
const before = text[offset - 1];
|
|
30801
|
+
return (before === '"' || before === "'") && text[offset + name.length] === before;
|
|
30802
|
+
}
|
|
30803
|
+
var WHOLE_CLASS_NAME = "the whole string is the class's name, as find_children's type, a comparison with get_global_name() or a list of accepted type names takes it, and it was not changed: if it names this class, it no longer matches anything after the rename, and no compile says so";
|
|
30804
|
+
var WHOLE_MEMBER_NAME = "the whole string is the member's name, as call, connect, has_method, get or set take it, and it was not changed: if it names this member, it no longer finds it after the rename, and no compile says so";
|
|
30785
30805
|
function declaresItsOwn(script, name, except) {
|
|
30786
30806
|
return script.occurrences(name).some((occurrence) => occurrence.kind === "code" && occurrence.offset !== except && occurrence.declaredAs !== null && occurrence.declaredAs !== "class_name");
|
|
30787
30807
|
}
|
|
@@ -30822,6 +30842,9 @@ function planClassRename(args) {
|
|
|
30822
30842
|
}
|
|
30823
30843
|
plan.mentionTheRest(texts, scripts, (file, occurrence, kind) => {
|
|
30824
30844
|
const script = scripts.get(file);
|
|
30845
|
+
if (kind === "string" && script !== undefined && isWholeString(script.text, occurrence.offset, oldName)) {
|
|
30846
|
+
return WHOLE_CLASS_NAME;
|
|
30847
|
+
}
|
|
30825
30848
|
if (kind !== "code" || script === undefined) {
|
|
30826
30849
|
return;
|
|
30827
30850
|
}
|
|
@@ -30888,7 +30911,16 @@ function planMemberRename(args) {
|
|
|
30888
30911
|
}
|
|
30889
30912
|
}
|
|
30890
30913
|
planScenes(texts, heirs, kind, oldName, plan);
|
|
30891
|
-
plan.mentionTheRest(texts, scripts, (
|
|
30914
|
+
plan.mentionTheRest(texts, scripts, (file, occurrence, mentionKind) => {
|
|
30915
|
+
const script = scripts.get(file);
|
|
30916
|
+
if (mentionKind === "string" && script !== undefined && isWholeString(script.text, occurrence.offset, oldName)) {
|
|
30917
|
+
return WHOLE_MEMBER_NAME;
|
|
30918
|
+
}
|
|
30919
|
+
if (mentionKind !== "code") {
|
|
30920
|
+
return;
|
|
30921
|
+
}
|
|
30922
|
+
return occurrence.afterDot ? "not resolved to this member by the language server: another class with a member of the same name, or a value whose type the analyser could not tell" : "not resolved to this member by the language server";
|
|
30923
|
+
});
|
|
30892
30924
|
return plan.build();
|
|
30893
30925
|
}
|
|
30894
30926
|
function sectionsOf(text) {
|
|
@@ -31578,6 +31610,7 @@ async function renameSymbol(request, services) {
|
|
|
31578
31610
|
let plan;
|
|
31579
31611
|
let what;
|
|
31580
31612
|
let resolvedBy;
|
|
31613
|
+
let took = null;
|
|
31581
31614
|
let overrides = [];
|
|
31582
31615
|
let declaringFiles = [declaringPath];
|
|
31583
31616
|
if (isClass) {
|
|
@@ -31684,7 +31717,23 @@ async function renameSymbol(request, services) {
|
|
|
31684
31717
|
}
|
|
31685
31718
|
overrides = family.map((one) => one.file).filter((file) => file !== fileOf(root));
|
|
31686
31719
|
declaringFiles = family.map((one) => one.file);
|
|
31687
|
-
|
|
31720
|
+
took = { references: { asked: 0, ms: 0 }, definitions: { asked: 0, ms: 0 } };
|
|
31721
|
+
let resolved;
|
|
31722
|
+
try {
|
|
31723
|
+
resolved = await resolveMember(projectPath, texts, scripts, family, symbol, services, took);
|
|
31724
|
+
} catch (error) {
|
|
31725
|
+
if (!(error instanceof LSPTimeout)) {
|
|
31726
|
+
throw error;
|
|
31727
|
+
}
|
|
31728
|
+
return {
|
|
31729
|
+
ok: false,
|
|
31730
|
+
reason: `The editor's language server took the ${error.method} request for ${symbol} and had not answered it after ${String(error.seconds)}s, so nothing was renamed.${error.method === "textDocument/references" ? " Godot answers a references request by looking for the name's text in every script and resolving each match, so a name also written as a common local takes longest." : ""}`,
|
|
31731
|
+
advice: [
|
|
31732
|
+
"script_diagnostics on the declaring script says whether the language server is answering other requests",
|
|
31733
|
+
"The request that ran out is still being answered by the editor, and another sent now waits behind it"
|
|
31734
|
+
]
|
|
31735
|
+
};
|
|
31736
|
+
}
|
|
31688
31737
|
if (resolved === null) {
|
|
31689
31738
|
const first = family[0];
|
|
31690
31739
|
const script = first === undefined ? undefined : scripts.get(first.file);
|
|
@@ -31732,6 +31781,7 @@ async function renameSymbol(request, services) {
|
|
|
31732
31781
|
rewrites,
|
|
31733
31782
|
report: {
|
|
31734
31783
|
renamed: { what, from: symbol, to: newName, declaredIn: declaringPath, resolvedBy },
|
|
31784
|
+
...took === null ? {} : { languageServer: took },
|
|
31735
31785
|
...overrides.length > 0 ? { overridesRenamed: overrides } : {},
|
|
31736
31786
|
...moveTo === null ? {} : { moved: { from: declaringPath, to: moveTo } },
|
|
31737
31787
|
changed: changedReport(plan.edits, rewrites),
|
|
@@ -31740,7 +31790,7 @@ async function renameSymbol(request, services) {
|
|
|
31740
31790
|
}
|
|
31741
31791
|
};
|
|
31742
31792
|
}
|
|
31743
|
-
async function resolveMember(projectPath, texts, scripts, family, name, services) {
|
|
31793
|
+
async function resolveMember(projectPath, texts, scripts, family, name, services, took) {
|
|
31744
31794
|
const absoluteOf = new Map(texts.map((file) => [file.path, file.absolute]));
|
|
31745
31795
|
const key = (place) => `${place.file}:${place.offset}`;
|
|
31746
31796
|
const toResolved = (located) => {
|
|
@@ -31754,11 +31804,16 @@ async function resolveMember(projectPath, texts, scripts, family, name, services
|
|
|
31754
31804
|
if (script === undefined || absolute === undefined) {
|
|
31755
31805
|
return [];
|
|
31756
31806
|
}
|
|
31757
|
-
const
|
|
31758
|
-
|
|
31807
|
+
const started = performance.now();
|
|
31808
|
+
try {
|
|
31809
|
+
const answered = await services[question](absolute, script.text, script.lines.positionOf(place.offset));
|
|
31810
|
+
return answered.map(toResolved).filter((one) => one !== null);
|
|
31811
|
+
} finally {
|
|
31812
|
+
took[question].asked += 1;
|
|
31813
|
+
took[question].ms += Math.round(performance.now() - started);
|
|
31814
|
+
}
|
|
31759
31815
|
};
|
|
31760
31816
|
const ours = new Map;
|
|
31761
|
-
const settled = new Set;
|
|
31762
31817
|
const root = family[0];
|
|
31763
31818
|
if (root === undefined) {
|
|
31764
31819
|
return [];
|
|
@@ -31774,23 +31829,13 @@ async function resolveMember(projectPath, texts, scripts, family, name, services
|
|
|
31774
31829
|
for (const script of scripts.values()) {
|
|
31775
31830
|
for (const occurrence of script.occurrences(name)) {
|
|
31776
31831
|
const place = { file: script.path, offset: occurrence.offset };
|
|
31777
|
-
if (occurrence.kind !== "code" || ours.has(key(place))
|
|
31832
|
+
if (occurrence.kind !== "code" || ours.has(key(place))) {
|
|
31778
31833
|
continue;
|
|
31779
31834
|
}
|
|
31780
31835
|
const declared = await ask("definitions", place);
|
|
31781
31836
|
const only = declared.length === 1 ? declared[0] : undefined;
|
|
31782
|
-
if (only
|
|
31783
|
-
|
|
31784
|
-
continue;
|
|
31785
|
-
}
|
|
31786
|
-
const isOurs = declarations.has(key(only));
|
|
31787
|
-
const same = [place, ...await ask("references", place)];
|
|
31788
|
-
for (const one of same) {
|
|
31789
|
-
if (isOurs) {
|
|
31790
|
-
ours.set(key(one), one);
|
|
31791
|
-
} else {
|
|
31792
|
-
settled.add(key(one));
|
|
31793
|
-
}
|
|
31837
|
+
if (only !== undefined && declarations.has(key(only))) {
|
|
31838
|
+
ours.set(key(place), place);
|
|
31794
31839
|
}
|
|
31795
31840
|
}
|
|
31796
31841
|
}
|
|
@@ -31846,8 +31891,9 @@ ${mention.kind}`;
|
|
|
31846
31891
|
full.push(mention);
|
|
31847
31892
|
}
|
|
31848
31893
|
}
|
|
31894
|
+
const loudFirst = (mention) => mention.kind === "string" && mention.why !== undefined ? 0 : 1;
|
|
31849
31895
|
return {
|
|
31850
|
-
leftAlone: full,
|
|
31896
|
+
leftAlone: full.sort((a, b) => loudFirst(a) - loudFirst(b)),
|
|
31851
31897
|
...prose.size > 0 ? { leftAloneInProse: [...prose.values()] } : {}
|
|
31852
31898
|
};
|
|
31853
31899
|
}
|
|
@@ -33037,7 +33083,7 @@ var TOOL_SPECS = [
|
|
|
33037
33083
|
},
|
|
33038
33084
|
{
|
|
33039
33085
|
name: "script_edit",
|
|
33040
|
-
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a find_children
|
|
33086
|
+
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment or a line of a text file with nothing more to say is given under leftAloneInProse by file and line instead. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
|
|
33041
33087
|
parameters: {
|
|
33042
33088
|
projectPath: PROJECT_PATH,
|
|
33043
33089
|
scriptPath: SCRIPT_PATH,
|
|
@@ -38553,7 +38599,9 @@ class GodotServer {
|
|
|
38553
38599
|
}
|
|
38554
38600
|
return await question(client);
|
|
38555
38601
|
} catch (error) {
|
|
38556
|
-
|
|
38602
|
+
if (!(error instanceof LSPTimeout)) {
|
|
38603
|
+
languageServer.problem = normalizeLSPError(error, client.port);
|
|
38604
|
+
}
|
|
38557
38605
|
throw error;
|
|
38558
38606
|
}
|
|
38559
38607
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.35",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|