Skip to content

Commit f9c9778

Browse files
committed
feat: Add help and autocomplete system for OIE scripting
- Add phantom.help() function for interactive documentation - Add phantom.autocomplete() function for autocomplete suggestions - Comprehensive help documentation for all operations - Support for category-level and operation-level help - Designed for OIE environments without native autocomplete - Usage examples: - phantom.help() - Show all categories - phantom.help('maps') - Show map operations - phantom.help('strings.operation') - Show string methods - phantom.autocomplete() - Get all categories - phantom.autocomplete('maps') - Get map operations - 12 new test cases added - All 310 tests passing
1 parent c395618 commit f9c9778

2 files changed

Lines changed: 446 additions & 0 deletions

File tree

phantom.js

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,357 @@
2626
return phantom;
2727
};
2828

29+
/* --------------------------------------------------
30+
* HELP & AUTOCOMPLETE SYSTEM
31+
* For OIE scripting environments without native autocomplete
32+
* -------------------------------------------------- */
33+
34+
// Help documentation for all operations
35+
var helpDocs = {
36+
maps: {
37+
description: "Map operations for storing and retrieving data",
38+
operations: {
39+
channel: {
40+
description: "Channel map operations (save, get, exists, delete)",
41+
methods: {
42+
save: "Save a value to channel map: phantom.maps.channel.save(key, value)",
43+
get: "Get a value from channel map: phantom.maps.channel.get(key)",
44+
exists: "Check if key exists: phantom.maps.channel.exists(key)",
45+
delete: "Delete a key: phantom.maps.channel.delete(key)"
46+
}
47+
},
48+
global: {
49+
description: "Global map operations (save, get, exists, delete)",
50+
methods: {
51+
save: "Save a value to global map: phantom.maps.global.save(key, value)",
52+
get: "Get a value from global map: phantom.maps.global.get(key)",
53+
exists: "Check if key exists: phantom.maps.global.exists(key)",
54+
delete: "Delete a key: phantom.maps.global.delete(key)"
55+
}
56+
},
57+
connector: {
58+
description: "Connector map operations (save, get, exists, delete)",
59+
methods: {
60+
save: "Save a value to connector map: phantom.maps.connector.save(key, value)",
61+
get: "Get a value from connector map: phantom.maps.connector.get(key)",
62+
exists: "Check if key exists: phantom.maps.connector.exists(key)",
63+
delete: "Delete a key: phantom.maps.connector.delete(key)"
64+
}
65+
},
66+
response: {
67+
description: "Response map operations (save, get, exists, delete) - Available only in response context",
68+
methods: {
69+
save: "Save a value to response map: phantom.maps.response.save(key, value)",
70+
get: "Get a value from response map: phantom.maps.response.get(key)",
71+
exists: "Check if key exists: phantom.maps.response.exists(key)",
72+
delete: "Delete a key: phantom.maps.response.delete(key)"
73+
}
74+
},
75+
configuration: {
76+
description: "Configuration map operations (read-only: get, exists)",
77+
methods: {
78+
get: "Get a value from configuration map: phantom.maps.configuration.get(key)",
79+
exists: "Check if key exists: phantom.maps.configuration.exists(key)"
80+
}
81+
}
82+
}
83+
},
84+
strings: {
85+
description: "String operations for text manipulation",
86+
operations: {
87+
operation: {
88+
description: "String utility functions",
89+
methods: {
90+
trim: "Remove whitespace from both ends: phantom.strings.operation.trim(str)",
91+
leftPad: "Pad string on the left: phantom.strings.operation.leftPad(str, count, padChar)",
92+
rightPad: "Pad string on the right: phantom.strings.operation.rightPad(str, count, padChar)",
93+
find: "Find substring position: phantom.strings.operation.find(str, search)",
94+
replace: "Replace substring: phantom.strings.operation.replace(str, search, replace)",
95+
split: "Split string into array: phantom.strings.operation.split(str, delimiter)",
96+
substring: "Extract substring: phantom.strings.operation.substring(str, start, end)",
97+
toUpperCase: "Convert to uppercase: phantom.strings.operation.toUpperCase(str)",
98+
toLowerCase: "Convert to lowercase: phantom.strings.operation.toLowerCase(str)",
99+
capitalize: "Capitalize first letter: phantom.strings.operation.capitalize(str)",
100+
reverse: "Reverse string: phantom.strings.operation.reverse(str)",
101+
length: "Get string length: phantom.strings.operation.length(str)",
102+
startsWith: "Check if starts with: phantom.strings.operation.startsWith(str, prefix)",
103+
endsWith: "Check if ends with: phantom.strings.operation.endsWith(str, suffix)",
104+
contains: "Check if contains: phantom.strings.operation.contains(str, search)",
105+
repeat: "Repeat string: phantom.strings.operation.repeat(str, count)",
106+
remove: "Remove substring: phantom.strings.operation.remove(str, search)",
107+
isEmpty: "Check if empty: phantom.strings.operation.isEmpty(str)",
108+
isBlank: "Check if blank: phantom.strings.operation.isBlank(str)",
109+
wordwrap: "Wrap text to lines: phantom.strings.operation.wordwrap(str, width)"
110+
}
111+
},
112+
chain: {
113+
description: "String chaining API for fluent operations",
114+
methods: {
115+
value: "Get final value: phantom.strings.chain(str).value()",
116+
toNumberChain: "Convert to number chain: phantom.strings.chain(str).toNumberChain()"
117+
}
118+
}
119+
}
120+
},
121+
numbers: {
122+
description: "Number operations for mathematical calculations",
123+
operations: {
124+
operation: {
125+
description: "Number utility functions",
126+
methods: {
127+
parse: "Parse string to number: phantom.numbers.operation.parse(str)",
128+
isNumber: "Check if number: phantom.numbers.operation.isNumber(value)",
129+
add: "Add two numbers: phantom.numbers.operation.add(a, b)",
130+
subtract: "Subtract: phantom.numbers.operation.subtract(a, b)",
131+
multiply: "Multiply: phantom.numbers.operation.multiply(a, b)",
132+
divide: "Divide: phantom.numbers.operation.divide(a, b)",
133+
round: "Round number: phantom.numbers.operation.round(num, decimals)",
134+
min: "Get minimum: phantom.numbers.operation.min(a, b)",
135+
max: "Get maximum: phantom.numbers.operation.max(a, b)",
136+
abs: "Absolute value: phantom.numbers.operation.abs(num)",
137+
ceil: "Round up: phantom.numbers.operation.ceil(num)",
138+
floor: "Round down: phantom.numbers.operation.floor(num)",
139+
sqrt: "Square root: phantom.numbers.operation.sqrt(num)",
140+
pow: "Power: phantom.numbers.operation.pow(base, exponent)",
141+
mod: "Modulo: phantom.numbers.operation.mod(a, b)",
142+
random: "Random number: phantom.numbers.operation.random()",
143+
randomInt: "Random integer: phantom.numbers.operation.randomInt(min, max)",
144+
between: "Check if between: phantom.numbers.operation.between(num, min, max)",
145+
clamp: "Clamp value: phantom.numbers.operation.clamp(num, min, max)",
146+
sign: "Get sign: phantom.numbers.operation.sign(num)",
147+
isEven: "Check if even: phantom.numbers.operation.isEven(num)",
148+
isOdd: "Check if odd: phantom.numbers.operation.isOdd(num)",
149+
isPositive: "Check if positive: phantom.numbers.operation.isPositive(num)",
150+
isNegative: "Check if negative: phantom.numbers.operation.isNegative(num)",
151+
isZero: "Check if zero: phantom.numbers.operation.isZero(num)",
152+
toFixed: "Format number: phantom.numbers.operation.toFixed(num, decimals)",
153+
truncate: "Truncate number: phantom.numbers.operation.truncate(num)"
154+
}
155+
},
156+
chain: {
157+
description: "Number chaining API for fluent operations",
158+
methods: {
159+
value: "Get final value: phantom.numbers.chain(num).value()",
160+
toStringChain: "Convert to string chain: phantom.numbers.chain(num).toStringChain()"
161+
}
162+
}
163+
}
164+
},
165+
json: {
166+
description: "JSON operations for parsing and manipulating JSON data",
167+
operations: {
168+
operation: {
169+
description: "JSON utility functions",
170+
methods: {
171+
parse: "Parse JSON string: phantom.json.operation.parse(str)",
172+
stringify: "Stringify object: phantom.json.operation.stringify(obj)",
173+
get: "Get value by path: phantom.json.operation.get(obj, path)",
174+
set: "Set value by path: phantom.json.operation.set(obj, path, value)",
175+
has: "Check if path exists: phantom.json.operation.has(obj, path)",
176+
remove: "Remove path: phantom.json.operation.remove(obj, path)",
177+
keys: "Get all keys: phantom.json.operation.keys(obj)",
178+
values: "Get all values: phantom.json.operation.values(obj)",
179+
size: "Get object size: phantom.json.operation.size(obj)",
180+
merge: "Merge objects: phantom.json.operation.merge(obj1, obj2)",
181+
isEmpty: "Check if empty: phantom.json.operation.isEmpty(obj)",
182+
isArray: "Check if array: phantom.json.operation.isArray(value)",
183+
isObject: "Check if object: phantom.json.operation.isObject(value)",
184+
toString: "Convert to string: phantom.json.operation.toString(obj)",
185+
prettyPrint: "Pretty print: phantom.json.operation.prettyPrint(obj)"
186+
}
187+
}
188+
}
189+
},
190+
base64: {
191+
description: "Base64 encoding and decoding operations",
192+
operations: {
193+
operation: {
194+
description: "Base64 utility functions",
195+
methods: {
196+
encode: "Encode to base64: phantom.base64.operation.encode(str)",
197+
decode: "Decode from base64: phantom.base64.operation.decode(str)"
198+
}
199+
}
200+
}
201+
},
202+
xml: {
203+
description: "XML operations for parsing and querying XML data",
204+
operations: {
205+
operation: {
206+
description: "XML utility functions",
207+
methods: {
208+
parse: "Parse XML string: phantom.xml.operation.parse(str)",
209+
stringify: "Stringify XML object: phantom.xml.operation.stringify(obj)",
210+
get: "Get value by XPath: phantom.xml.operation.get(xml, xpath)",
211+
has: "Check if XPath exists: phantom.xml.operation.has(xml, xpath)",
212+
toString: "Convert to string: phantom.xml.operation.toString(xml)"
213+
}
214+
}
215+
}
216+
},
217+
dates: {
218+
description: "Date and time operations using Java.time APIs",
219+
operations: {
220+
operation: {
221+
description: "Date utility functions",
222+
methods: {
223+
now: "Get current datetime: phantom.dates.operation.now()",
224+
today: "Get current date: phantom.dates.operation.today()",
225+
parse: "Parse date string: phantom.dates.operation.parse(str, format)",
226+
parseDateTime: "Parse datetime string: phantom.dates.operation.parseDateTime(str, format)",
227+
format: "Format date: phantom.dates.operation.format(date, format)",
228+
formatDateTime: "Format datetime: phantom.dates.operation.formatDateTime(dt, format)",
229+
getYear: "Get year: phantom.dates.operation.getYear(date)",
230+
getMonth: "Get month: phantom.dates.operation.getMonth(date)",
231+
getDay: "Get day: phantom.dates.operation.getDay(date)",
232+
getDayOfWeek: "Get day of week: phantom.dates.operation.getDayOfWeek(date)",
233+
add: "Add time: phantom.dates.operation.add(date, amount, unit)",
234+
subtract: "Subtract time: phantom.dates.operation.subtract(date, amount, unit)",
235+
between: "Calculate difference: phantom.dates.operation.between(date1, date2, unit)",
236+
isBefore: "Check if before: phantom.dates.operation.isBefore(date1, date2)",
237+
isAfter: "Check if after: phantom.dates.operation.isAfter(date1, date2)",
238+
isEqual: "Check if equal: phantom.dates.operation.isEqual(date1, date2)",
239+
startOfDay: "Get start of day: phantom.dates.operation.startOfDay(date)",
240+
endOfDay: "Get end of day: phantom.dates.operation.endOfDay(date)"
241+
}
242+
},
243+
format: {
244+
description: "Date format detection and utilities",
245+
methods: {
246+
detect: "Detect date format: phantom.dates.format.detect(dateString)"
247+
}
248+
},
249+
duration: {
250+
description: "Duration operations",
251+
methods: {
252+
between: "Calculate duration: phantom.dates.duration.between(dt1, dt2)",
253+
of: "Create duration: phantom.dates.duration.of(amount, unit)",
254+
add: "Add duration: phantom.dates.duration.add(dt, duration)",
255+
subtract: "Subtract duration: phantom.dates.duration.subtract(dt, duration)",
256+
toDays: "Convert to days: phantom.dates.duration.toDays(duration)",
257+
toHours: "Convert to hours: phantom.dates.duration.toHours(duration)",
258+
toMinutes: "Convert to minutes: phantom.dates.duration.toMinutes(duration)",
259+
toSeconds: "Convert to seconds: phantom.dates.duration.toSeconds(duration)",
260+
toMillis: "Convert to milliseconds: phantom.dates.duration.toMillis(duration)"
261+
}
262+
}
263+
}
264+
}
265+
};
266+
267+
// Help function - shows available operations
268+
phantom.help = function (path) {
269+
try {
270+
if (!path) {
271+
// Show all categories
272+
var output = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
273+
output += "📚 Phantom.js Help - Available Categories\n";
274+
output += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
275+
output += "Usage: phantom.help('category') or phantom.help('category.operation')\n\n";
276+
277+
for (var category in helpDocs) {
278+
output += "📦 phantom." + category + "\n";
279+
output += " " + helpDocs[category].description + "\n";
280+
output += " Example: phantom.help('" + category + "')\n\n";
281+
}
282+
283+
output += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
284+
return output;
285+
}
286+
287+
var parts = path.split('.');
288+
var current = helpDocs;
289+
290+
// Navigate through the path
291+
for (var i = 0; i < parts.length; i++) {
292+
if (current && current[parts[i]]) {
293+
current = current[parts[i]];
294+
} else if (current && current.operations && current.operations[parts[i]]) {
295+
current = current.operations[parts[i]];
296+
} else {
297+
return "❌ Path not found: phantom." + path + "\n\nUse phantom.help() to see all available categories.";
298+
}
299+
}
300+
301+
// Display the help
302+
var output = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
303+
output += "📚 phantom." + path + "\n";
304+
output += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
305+
306+
if (current.description) {
307+
output += "Description: " + current.description + "\n\n";
308+
}
309+
310+
if (current.methods) {
311+
output += "Available Methods:\n";
312+
output += "────────────────────────────────────────────────────\n";
313+
for (var method in current.methods) {
314+
output += "• " + method + "\n";
315+
output += " " + current.methods[method] + "\n\n";
316+
}
317+
} else if (current.operations) {
318+
output += "Available Operations:\n";
319+
output += "────────────────────────────────────────────────────\n";
320+
for (var op in current.operations) {
321+
output += "• " + op + "\n";
322+
if (current.operations[op].description) {
323+
output += " " + current.operations[op].description + "\n";
324+
}
325+
output += " Example: phantom.help('" + path + "." + op + "')\n\n";
326+
}
327+
}
328+
329+
output += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
330+
return output;
331+
} catch (e) {
332+
return "Error: " + (e.message || String(e));
333+
}
334+
};
335+
336+
// Autocomplete function - returns available options
337+
phantom.autocomplete = function (path) {
338+
try {
339+
if (!path) {
340+
// Return all top-level categories
341+
return Object.keys(helpDocs);
342+
}
343+
344+
var parts = path.split('.');
345+
var current = helpDocs;
346+
347+
// Navigate through the path
348+
for (var i = 0; i < parts.length; i++) {
349+
if (current && current[parts[i]]) {
350+
current = current[parts[i]];
351+
} else if (current && current.operations && current.operations[parts[i]]) {
352+
current = current.operations[parts[i]];
353+
} else {
354+
return [];
355+
}
356+
}
357+
358+
var options = [];
359+
360+
// If we have operations, return them
361+
if (current.operations) {
362+
options = Object.keys(current.operations);
363+
} else if (current.methods) {
364+
options = Object.keys(current.methods);
365+
} else if (typeof current === 'object') {
366+
// Return direct properties
367+
for (var key in current) {
368+
if (key !== 'description' && key !== 'operations' && key !== 'methods') {
369+
options.push(key);
370+
}
371+
}
372+
}
373+
374+
return options;
375+
} catch (e) {
376+
return [];
377+
}
378+
};
379+
29380
// Only log on error (as requested)
30381
function logError(msg) {
31382
if (typeof logger !== "undefined") {

0 commit comments

Comments
 (0)