@xano/xanoscript-language-server 11.8.5 → 11.9.0
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/lexer/comment.js
CHANGED
|
@@ -8,36 +8,26 @@ export const CommentToken = createUniqToken({
|
|
|
8
8
|
pattern: {
|
|
9
9
|
exec: (text, offset) => {
|
|
10
10
|
// Look for the `//` marker at the current offset
|
|
11
|
-
if (
|
|
11
|
+
if (text.charCodeAt(offset) !== 47 || text.charCodeAt(offset + 1) !== 47) {
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
if (offset
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
while (checkOffset >= 0) {
|
|
24
|
-
const char = text[checkOffset];
|
|
25
|
-
|
|
26
|
-
// If we find a newline, the comment is on its own line (possibly with leading whitespace)
|
|
27
|
-
if (char === "\n" || char === "\r") {
|
|
28
|
-
return text.slice(offset).match(/.*(?=\n|$)/);
|
|
15
|
+
// Check that the comment is on its own line (only whitespace before it)
|
|
16
|
+
if (offset > 0) {
|
|
17
|
+
let checkOffset = offset - 1;
|
|
18
|
+
while (checkOffset >= 0) {
|
|
19
|
+
const char = text.charCodeAt(checkOffset);
|
|
20
|
+
if (char === 10 || char === 13) break; // \n or \r → own line
|
|
21
|
+
if (char !== 32 && char !== 9) return null; // not space/tab → reject
|
|
22
|
+
checkOffset--;
|
|
29
23
|
}
|
|
30
|
-
|
|
31
|
-
// If we find anything other than space or tab, reject
|
|
32
|
-
if (char !== " " && char !== "\t") {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
checkOffset--;
|
|
37
24
|
}
|
|
38
25
|
|
|
39
|
-
//
|
|
40
|
-
|
|
26
|
+
// Find end of line without allocating a substring
|
|
27
|
+
let end = text.indexOf("\n", offset);
|
|
28
|
+
if (end === -1) end = text.length;
|
|
29
|
+
const match = text.slice(offset, end);
|
|
30
|
+
return [match];
|
|
41
31
|
},
|
|
42
32
|
},
|
|
43
33
|
// we don't capture more than one line
|
|
@@ -59,7 +59,7 @@ function createDiagnostics(parser, document) {
|
|
|
59
59
|
* @param {import('vscode-languageserver').Connection} connection
|
|
60
60
|
* @returns
|
|
61
61
|
*/
|
|
62
|
-
export function onDidChangeContent(params, connection) {
|
|
62
|
+
export function onDidChangeContent(params, connection, features = {}) {
|
|
63
63
|
const document = params.document;
|
|
64
64
|
|
|
65
65
|
if (!document) {
|
|
@@ -93,7 +93,7 @@ export function onDidChangeContent(params, connection) {
|
|
|
93
93
|
const diagnostics = createDiagnostics(parser, document);
|
|
94
94
|
|
|
95
95
|
// Run cross-file validation and append as warnings
|
|
96
|
-
if (parser.__symbolTable?.references) {
|
|
96
|
+
if (features.crossFileValidation !== false && parser.__symbolTable?.references) {
|
|
97
97
|
const crossFileWarnings = crossFileValidate(
|
|
98
98
|
parser.__symbolTable.references,
|
|
99
99
|
workspaceIndex,
|
|
@@ -111,7 +111,7 @@ export function onDidChangeContent(params, connection) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// Run variable validation and append warnings/hints
|
|
114
|
-
if (parser.__symbolTable?.varDeclarations) {
|
|
114
|
+
if (features.variableValidation !== false && parser.__symbolTable?.varDeclarations) {
|
|
115
115
|
const varResult = validateVariables(
|
|
116
116
|
parser.__symbolTable,
|
|
117
117
|
lexResult.tokens,
|
package/package.json
CHANGED
package/parser/task_parser.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ActiveToken } from "../lexer/api_group.js";
|
|
2
|
+
import { CommentToken } from "../lexer/comment.js";
|
|
2
3
|
import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
|
|
3
4
|
import { StringLiteral } from "../lexer/literal.js";
|
|
4
5
|
import { DataSourceToken, TaskToken } from "../lexer/task.js";
|
|
@@ -32,7 +33,7 @@ export function taskDeclaration($) {
|
|
|
32
33
|
$.MANY(() => {
|
|
33
34
|
$.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
|
|
34
35
|
$.OR2([
|
|
35
|
-
{ ALT: () => $.
|
|
36
|
+
{ ALT: () => $.CONSUME(CommentToken) },
|
|
36
37
|
{
|
|
37
38
|
GATE: () => !hasActive,
|
|
38
39
|
ALT: () => {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
task claim_reminders {
|
|
2
|
+
description = "Runs every 6 hours to send reminders for claims stalled at the assessment, review, or approval workflow stages."
|
|
3
|
+
|
|
4
|
+
stack {
|
|
5
|
+
debug.log {
|
|
6
|
+
value = "Claim reminders task started"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// --- Cutoff timestamps ---
|
|
10
|
+
|
|
11
|
+
var $forty_eight_hours_ago {
|
|
12
|
+
value = now|transform_timestamp:"-48 hours"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var $twenty_four_hours_ago {
|
|
16
|
+
value = now|transform_timestamp:"-24 hours"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
var $twelve_hours_ago {
|
|
20
|
+
value = now|transform_timestamp:"-12 hours"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var $assessment_reminder_count {
|
|
24
|
+
value = 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var $review_reminder_count {
|
|
28
|
+
value = 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var $approval_reminder_count {
|
|
32
|
+
value = 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// === Step 1: Garage Assessment Reminders (stalled > 48h) ===
|
|
36
|
+
|
|
37
|
+
db.query claim {
|
|
38
|
+
where = ($db.claim.workflow_status == "assessment" && $db.claim.updated_at < $forty_eight_hours_ago)
|
|
39
|
+
} as $assessment_claims
|
|
40
|
+
|
|
41
|
+
foreach ($assessment_claims) {
|
|
42
|
+
each as $claim {
|
|
43
|
+
try_catch {
|
|
44
|
+
try {
|
|
45
|
+
function.run "communication/send_notification" {
|
|
46
|
+
input = {
|
|
47
|
+
user_id : $claim.garage_id
|
|
48
|
+
claim_id: $claim.id
|
|
49
|
+
message : "Reminder: Vehicle assessment for claim " ~ $claim.claim_number ~ " is awaiting your action. Please complete the assessment report at your earliest convenience."
|
|
50
|
+
subject : "Assessment Reminder - Claim " ~ $claim.claim_number
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var.update $assessment_reminder_count {
|
|
55
|
+
value = $assessment_reminder_count + 1
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
debug.log {
|
|
60
|
+
value = {
|
|
61
|
+
error : "Failed to send assessment reminder"
|
|
62
|
+
claim_number: $claim.claim_number
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
debug.log {
|
|
71
|
+
value = "Assessment reminders sent: " ~ $assessment_reminder_count
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// === Step 2: Agent Review Reminders (stalled > 24h) ===
|
|
75
|
+
|
|
76
|
+
db.query claim {
|
|
77
|
+
where = ($db.claim.workflow_status == "review" && $db.claim.updated_at < $twenty_four_hours_ago)
|
|
78
|
+
} as $review_claims
|
|
79
|
+
|
|
80
|
+
foreach ($review_claims) {
|
|
81
|
+
each as $claim {
|
|
82
|
+
try_catch {
|
|
83
|
+
try {
|
|
84
|
+
function.run "communication/send_notification" {
|
|
85
|
+
input = {
|
|
86
|
+
user_id : $claim.assigned_agent_id
|
|
87
|
+
claim_id: $claim.id
|
|
88
|
+
message : "Reminder: Claim " ~ $claim.claim_number ~ " is awaiting your review. Please complete your review to keep the claim moving forward."
|
|
89
|
+
subject : "Review Reminder - Claim " ~ $claim.claim_number
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var.update $review_reminder_count {
|
|
94
|
+
value = $review_reminder_count + 1
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
debug.log {
|
|
99
|
+
value = {
|
|
100
|
+
error : "Failed to send review reminder"
|
|
101
|
+
claim_number: $claim.claim_number
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
debug.log {
|
|
110
|
+
value = "Review reminders sent: " ~ $review_reminder_count
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// === Step 3: Agent Approval Reminders - URGENT (stalled > 12h) ===
|
|
114
|
+
|
|
115
|
+
db.query claim {
|
|
116
|
+
where = ($db.claim.workflow_status == "approval" && $db.claim.updated_at < $twelve_hours_ago)
|
|
117
|
+
} as $approval_claims
|
|
118
|
+
|
|
119
|
+
foreach ($approval_claims) {
|
|
120
|
+
each as $claim {
|
|
121
|
+
try_catch {
|
|
122
|
+
try {
|
|
123
|
+
function.run "communication/send_notification" {
|
|
124
|
+
input = {
|
|
125
|
+
user_id : $claim.assigned_agent_id
|
|
126
|
+
claim_id: $claim.id
|
|
127
|
+
message : "URGENT: Claim " ~ $claim.claim_number ~ " requires your immediate approval. This claim has been pending approval for over 12 hours. Please review and take action as soon as possible."
|
|
128
|
+
subject : "URGENT: Approval Required - Claim " ~ $claim.claim_number
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
var.update $approval_reminder_count {
|
|
133
|
+
value = $approval_reminder_count + 1
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
debug.log {
|
|
138
|
+
value = {
|
|
139
|
+
error : "Failed to send approval reminder"
|
|
140
|
+
claim_number: $claim.claim_number
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
debug.log {
|
|
149
|
+
value = "Approval reminders sent: " ~ $approval_reminder_count
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// === Step 4: Final summary ===
|
|
153
|
+
|
|
154
|
+
debug.log {
|
|
155
|
+
value = {
|
|
156
|
+
assessment_reminders_sent: $assessment_reminder_count
|
|
157
|
+
review_reminders_sent : $review_reminder_count
|
|
158
|
+
approval_reminders_sent : $approval_reminder_count
|
|
159
|
+
total_reminders_sent : ($assessment_reminder_count + $review_reminder_count + $approval_reminder_count)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
schedule = [{starts_on: 2026-01-01 00:00:00+0000, freq: 21600}]
|
|
165
|
+
}
|
package/server.js
CHANGED
|
@@ -20,6 +20,20 @@ import { xanoscriptParser } from "./parser/parser.js";
|
|
|
20
20
|
import { getSchemeFromContent } from "./utils.js";
|
|
21
21
|
import { workspaceIndex } from "./workspace/workspaceIndex.js";
|
|
22
22
|
|
|
23
|
+
// Feature toggles for debugging memory issues.
|
|
24
|
+
// Disable one at a time to isolate the culprit.
|
|
25
|
+
const FEATURES = {
|
|
26
|
+
workspaceScan: true, // initial .xs file scan on startup
|
|
27
|
+
fileWatcher: true, // onDidChangeWatchedFiles
|
|
28
|
+
completion: true, // onCompletion (content assist)
|
|
29
|
+
semanticTokens: true, // semantic highlighting
|
|
30
|
+
hover: true, // onHover
|
|
31
|
+
definition: true, // onDefinition (go-to-definition)
|
|
32
|
+
diagnostics: true, // onDidChangeContent (parse + diagnostics)
|
|
33
|
+
crossFileValidation: true, // cross-file reference validation
|
|
34
|
+
variableValidation: true, // variable usage validation
|
|
35
|
+
};
|
|
36
|
+
|
|
23
37
|
// Create a connection to the VS Code client
|
|
24
38
|
const connection = createConnection(ProposedFeatures.all);
|
|
25
39
|
|
|
@@ -31,12 +45,14 @@ connection.onInitialize((params) => {
|
|
|
31
45
|
connection.console.log("XanoScript Language Server initialized");
|
|
32
46
|
|
|
33
47
|
// Scan workspace for .xs files to populate the index
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
if (FEATURES.workspaceScan) {
|
|
49
|
+
if (params.workspaceFolders) {
|
|
50
|
+
for (const folder of params.workspaceFolders) {
|
|
51
|
+
scanWorkspaceFolder(folder.uri);
|
|
52
|
+
}
|
|
53
|
+
} else if (params.rootUri) {
|
|
54
|
+
scanWorkspaceFolder(params.rootUri);
|
|
37
55
|
}
|
|
38
|
-
} else if (params.rootUri) {
|
|
39
|
-
scanWorkspaceFolder(params.rootUri);
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
return {
|
|
@@ -142,6 +158,8 @@ function indexFilesInBatches(filePaths) {
|
|
|
142
158
|
}
|
|
143
159
|
|
|
144
160
|
connection.onDidChangeWatchedFiles((params) => {
|
|
161
|
+
if (!FEATURES.fileWatcher) return;
|
|
162
|
+
connection.console.log(`[FILEWATCHER] ${params.changes.length} changes`);
|
|
145
163
|
for (const change of params.changes) {
|
|
146
164
|
if (!change.uri.endsWith(".xs")) continue;
|
|
147
165
|
switch (change.type) {
|
|
@@ -164,19 +182,29 @@ connection.onDidChangeWatchedFiles((params) => {
|
|
|
164
182
|
}
|
|
165
183
|
});
|
|
166
184
|
|
|
167
|
-
connection.onHover((params) =>
|
|
168
|
-
|
|
185
|
+
connection.onHover((params) => {
|
|
186
|
+
if (!FEATURES.hover) return null;
|
|
187
|
+
connection.console.log(
|
|
188
|
+
`[HOVER] ${params.textDocument.uri} pos=${JSON.stringify(params.position)}`,
|
|
189
|
+
);
|
|
190
|
+
return onHover(params, documents);
|
|
191
|
+
});
|
|
192
|
+
connection.onCompletion((params) => {
|
|
193
|
+
if (!FEATURES.completion) return null;
|
|
194
|
+
connection.console.log(
|
|
195
|
+
`[COMPLETION] ${params.textDocument.uri} pos=${JSON.stringify(params.position)}`,
|
|
196
|
+
);
|
|
197
|
+
return onCompletion(params, documents);
|
|
198
|
+
});
|
|
169
199
|
connection.onDefinition((params) => {
|
|
200
|
+
if (!FEATURES.definition) return null;
|
|
201
|
+
connection.console.log(`[DEFINITION] ${params.textDocument.uri}`);
|
|
170
202
|
const document = documents.get(params.textDocument.uri);
|
|
171
203
|
if (!document) return null;
|
|
172
204
|
|
|
173
205
|
const text = document.getText();
|
|
174
206
|
const offset = document.offsetAt(params.position);
|
|
175
|
-
const cached = documentCache.getOrParse(
|
|
176
|
-
document.uri,
|
|
177
|
-
document.version,
|
|
178
|
-
text,
|
|
179
|
-
);
|
|
207
|
+
const cached = documentCache.getOrParse(document.uri, document.version, text);
|
|
180
208
|
const symbolTable = cached.parser?.__symbolTable;
|
|
181
209
|
const result = findDefinition(text, offset, workspaceIndex, symbolTable);
|
|
182
210
|
|
|
@@ -197,12 +225,18 @@ connection.onDefinition((params) => {
|
|
|
197
225
|
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
198
226
|
};
|
|
199
227
|
});
|
|
200
|
-
connection.onRequest("textDocument/semanticTokens/full", (params) =>
|
|
201
|
-
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
228
|
+
connection.onRequest("textDocument/semanticTokens/full", (params) => {
|
|
229
|
+
if (!FEATURES.semanticTokens) return null;
|
|
230
|
+
connection.console.log(`[SEMANTIC] ${params.textDocument.uri}`);
|
|
231
|
+
return onSemanticCheck(params, documents, SemanticTokensBuilder);
|
|
232
|
+
});
|
|
233
|
+
documents.onDidChangeContent((params) => {
|
|
234
|
+
if (!FEATURES.diagnostics) return;
|
|
235
|
+
connection.console.log(
|
|
236
|
+
`[DIAGNOSTICS] ${params.document.uri} v${params.document.version}`,
|
|
237
|
+
);
|
|
238
|
+
onDidChangeContent(params, connection, FEATURES);
|
|
239
|
+
});
|
|
206
240
|
documents.onDidClose((params) => {
|
|
207
241
|
const uri = params.document.uri;
|
|
208
242
|
documentCache.invalidate(uri);
|