Skip to content

Commit c8b3142

Browse files
CopilotDaanV2
andauthored
fix: handle quoted wildcard "*" in scoreboard commands to prevent codeLens crash (#482)
Using `scoreboard players reset "*" test` crashes the `textDocument/codeLens` request because the quoted `"*"` was not recognized as a wildcard, causing it to be registered as a fake entity with id `*`, which then broke regex construction in the codeLens handler (`/\b*\b/g` → "Nothing to repeat"). ## Changes - **`objective/process.ts`** — `CheckPlayer` now uses `Text.UnQuote()` before comparing against `*`, so `"*"` is correctly excluded from fake entity registration - **`commands.ts`** — `mcfunction_diagnoseparameter` wildcard check extended to also match `"*"`, skipping diagnosis for quoted wildcards - **`selector.ts`** — `Selector.isSelector` now accepts `"*"` as valid when `wildcard: true` - **`code-lens/on-request.ts`** — Added `escapeRegex()` to sanitize IDs before inserting into `RegExp` constructors, guarding against any future special-character IDs slipping through ```mcfunction # All of these now work without errors: scoreboard players reset * test scoreboard players reset "*" test ``` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DaanV2 <2393905+DaanV2@users.noreply.github.com>
1 parent 345fa64 commit c8b3142

6 files changed

Lines changed: 30 additions & 5 deletions

File tree

ide/base/server/src/lsp/code-lens/on-request.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,15 @@ function isJson(doc: TextDocument) {
6969
}
7070

7171
function defaultRegex(id: string) {
72-
return new RegExp(`\\b${id}\\b`, 'g');
72+
return new RegExp(`\\b${escapeRegex(id)}\\b`, 'g');
7373
}
7474

7575
function selectorThing(id: string, doc: TextDocument) {
76-
return isJson(doc) ? new RegExp(`\\b(${id}=|=${id})\\b`, 'g') : defaultRegex(id);
76+
return isJson(doc) ? new RegExp(`\\b(${escapeRegex(id)}=|=${escapeRegex(id)})\\b`, 'g') : defaultRegex(id);
77+
}
78+
79+
function escapeRegex(id: string): string {
80+
return id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7781
}
7882

7983
function config(pd: ProjectData) {

packages/bedrock-diagnoser/src/diagnostics/behavior-pack/mcfunction/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ function mcfunction_diagnoseparameter(
329329
if (pattern.options) {
330330
//If wildcard is allowed and the text is an wildcard, then skip diagnose
331331
if (pattern.options.wildcard === true) {
332-
if (data.text === '*') return;
332+
if (data.text === '*' || data.text === '"*"') return;
333333
}
334334

335335
//If accepted values is filled in and the text is a match, then skip diagnose

packages/bedrock-project/src/project/general/types/commands/commands.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,22 @@ scoreboard players set global id 0`,
159159
expect(P.general.objectives.count()).toEqual(1);
160160
expect(P.general.objectives.has('score')).toBeTruthy();
161161
});
162+
163+
it('scoreboard wildcard star should not register as fake entity', () => {
164+
const P = new ProjectData(new TextProjectContext());
165+
166+
P.behaviorPacks.add('c:\\bp', MCProject.createEmpty(), {} as Manifest);
167+
168+
const doc: TextDocument = {
169+
uri: 'c:\\bp\\functions\\example.mcfunction',
170+
getText: () => `scoreboard players reset * test
171+
scoreboard players reset "*" test`,
172+
};
173+
174+
P.process(doc);
175+
176+
// Neither bare * nor quoted "*" should be registered as a fake entity
177+
expect(P.general.fakeEntities.count()).toEqual(0);
178+
expect(P.general.fakeEntities.has('*')).toBeFalsy();
179+
});
162180
});

packages/bedrock-project/src/project/general/types/objective/process.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Command } from 'bc-minecraft-bedrock-command';
22
import { Location } from 'bc-minecraft-bedrock-shared';
33
import { Documentation, TextDocument } from '../../../../types';
4+
import { Text } from '../../../../types';
45
import { GeneralCollection } from '../../general';
56
import { GeneralInfo } from '../general-info';
67

@@ -68,7 +69,7 @@ function CheckPlayer(Com: Command, doc: TextDocument): GeneralInfo | undefined {
6869
if (Com.parameters.length > 3) {
6970
const Selector = Com.parameters[3];
7071

71-
if (!Selector.text.startsWith('@') && Selector.text !== '*') {
72+
if (!Selector.text.startsWith('@') && Text.UnQuote(Selector.text) !== '*') {
7273
return GeneralInfo.create(
7374
Selector.text,
7475
Location.create(doc.uri, Selector.offset),

packages/bedrock-types/src/minecraft/selector/selector.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ describe('Selector', () => {
3030
['"Fake player"', false, true],
3131
['@initiator', true, true],
3232
['*', true, true],
33+
['"*"', true, true],
3334
];
3435
const invalid: Parameters<typeof Selector.isSelector>[] = [
3536
['@x', true, true],
3637
['test', false, false],
3738
['"Fake player"', false, false],
3839
['*', false, false],
40+
['"*"', false, false],
3941
];
4042

4143
valid.forEach((args) => {

packages/bedrock-types/src/minecraft/selector/selector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export namespace Selector {
5858
*/
5959
export function isSelector(value: string, wildcard?: boolean, allowFakePlayer?: boolean): boolean {
6060
if (wildcard === true) {
61-
if (value === '*') return true;
61+
if (value === '*' || value === '"*"') return true;
6262
}
6363

6464
if (!value.startsWith('@')) {

0 commit comments

Comments
 (0)