Skip to content

Commit 695dc3f

Browse files
committed
Improve sorting of input/output arguments
1 parent 558f51b commit 695dc3f

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

extensions/ql-vscode/src/data-extensions-editor/auto-model.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export function parsePredictedClassifications(
116116
// Order the sinks by the input alphabetically. This will ensure that the first argument is always
117117
// first in the list of sinks, the second argument is always second, etc.
118118
// If we get back "Argument[1]" and "Argument[3]", "Argument[1]" should always be first
119-
sinks.sort((a, b) => (a.input ?? "").localeCompare(b.input ?? ""));
119+
sinks.sort((a, b) => compareInputOutput(a.input ?? "", b.input ?? ""));
120120

121121
const sink = sinks[0];
122122

@@ -159,3 +159,60 @@ function toMethodClassification(modeledMethod: ModeledMethod): Classification {
159159
function toFullMethodSignature(method: Method): string {
160160
return `${method.package}.${method.type}#${method.name}${method.signature}`;
161161
}
162+
163+
const argumentRegex = /^Argument\[(\d+)]$/;
164+
165+
// Argument[this] is before ReturnValue
166+
const nonNumericArgumentOrder = ["Argument[this]", "ReturnValue"];
167+
168+
/**
169+
* Compare two inputs or outputs matching `Argument[<number>]`, `Argument[this]`, or `ReturnValue`.
170+
* If they are the same, return 0. If a is less than b, returns a negative number.
171+
* If a is greater than b, returns a positive number.
172+
*/
173+
export function compareInputOutput(a: string, b: string): number {
174+
if (a === b) {
175+
return 0;
176+
}
177+
178+
const aMatch = a.match(argumentRegex);
179+
const bMatch = b.match(argumentRegex);
180+
181+
// Numeric arguments are always first
182+
if (aMatch && !bMatch) {
183+
return -1;
184+
}
185+
if (!aMatch && bMatch) {
186+
return 1;
187+
}
188+
189+
// Neither is an argument
190+
if (!aMatch && !bMatch) {
191+
const aIndex = nonNumericArgumentOrder.indexOf(a);
192+
const bIndex = nonNumericArgumentOrder.indexOf(b);
193+
194+
// If either one is unknown, it is sorted last
195+
if (aIndex === -1 && bIndex === -1) {
196+
return a.localeCompare(b);
197+
}
198+
if (aIndex === -1) {
199+
return 1;
200+
}
201+
if (bIndex === -1) {
202+
return -1;
203+
}
204+
205+
return aIndex - bIndex;
206+
}
207+
208+
// This case shouldn't happen, but makes TypeScript happy
209+
if (!aMatch || !bMatch) {
210+
return 0;
211+
}
212+
213+
// Both are arguments
214+
const aIndex = parseInt(aMatch[1]);
215+
const bIndex = parseInt(bMatch[1]);
216+
217+
return aIndex - bIndex;
218+
}

extensions/ql-vscode/test/unit-tests/data-extensions-editor/auto-model.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
compareInputOutput,
23
createAutoModelRequest,
34
parsePredictedClassifications,
45
} from "../../../src/data-extensions-editor/auto-model";
@@ -381,3 +382,49 @@ describe("parsePredictedClassifications", () => {
381382
});
382383
});
383384
});
385+
386+
describe("compareInputOutput", () => {
387+
it("with two small numeric arguments", () => {
388+
expect(
389+
compareInputOutput("Argument[0]", "Argument[1]"),
390+
).toBeLessThanOrEqual(-1);
391+
});
392+
393+
it("with one larger non-alphabetic argument", () => {
394+
expect(
395+
compareInputOutput("Argument[10]", "Argument[2]"),
396+
).toBeGreaterThanOrEqual(1);
397+
});
398+
399+
it("with one non-numeric arguments", () => {
400+
expect(
401+
compareInputOutput("Argument[5]", "Argument[this]"),
402+
).toBeLessThanOrEqual(-1);
403+
});
404+
405+
it("with two non-numeric arguments", () => {
406+
expect(
407+
compareInputOutput("ReturnValue", "Argument[this]"),
408+
).toBeGreaterThanOrEqual(1);
409+
});
410+
411+
it("with one unknown argument in the a position", () => {
412+
expect(
413+
compareInputOutput("FooBar", "Argument[this]"),
414+
).toBeGreaterThanOrEqual(1);
415+
});
416+
417+
it("with one unknown argument in the b position", () => {
418+
expect(compareInputOutput("Argument[this]", "FooBar")).toBeLessThanOrEqual(
419+
-1,
420+
);
421+
});
422+
423+
it("with one empty string arguments", () => {
424+
expect(compareInputOutput("Argument[5]", "")).toBeLessThanOrEqual(-1);
425+
});
426+
427+
it("with two unknown arguments", () => {
428+
expect(compareInputOutput("FooBar", "BarFoo")).toBeGreaterThanOrEqual(1);
429+
});
430+
});

0 commit comments

Comments
 (0)