trigger_system 1.0.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/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/browser/index.browser.js +48 -0
- package/dist/browser/index.browser.js.map +122 -0
- package/dist/cli/lsp-server.d.ts +3 -0
- package/dist/cli/lsp-server.d.ts.map +1 -0
- package/dist/cli/validate.d.ts +2 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/core/action-registry.d.ts +12 -0
- package/dist/core/action-registry.d.ts.map +1 -0
- package/dist/core/context-adapter.d.ts +23 -0
- package/dist/core/context-adapter.d.ts.map +1 -0
- package/dist/core/dependency-graph.d.ts +18 -0
- package/dist/core/dependency-graph.d.ts.map +1 -0
- package/dist/core/engine.d.ts +24 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/event-queue.d.ts +25 -0
- package/dist/core/event-queue.d.ts.map +1 -0
- package/dist/core/expression-engine.d.ts +31 -0
- package/dist/core/expression-engine.d.ts.map +1 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/persistence-browser.d.ts +20 -0
- package/dist/core/persistence-browser.d.ts.map +1 -0
- package/dist/core/persistence.d.ts +34 -0
- package/dist/core/persistence.d.ts.map +1 -0
- package/dist/core/persistence.node.d.ts +18 -0
- package/dist/core/persistence.node.d.ts.map +1 -0
- package/dist/core/plugin-manager.d.ts +15 -0
- package/dist/core/plugin-manager.d.ts.map +1 -0
- package/dist/core/rule-engine.d.ts +39 -0
- package/dist/core/rule-engine.d.ts.map +1 -0
- package/dist/core/state-manager.d.ts +41 -0
- package/dist/core/state-manager.d.ts.map +1 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/validator.d.ts +2433 -0
- package/dist/domain/validator.d.ts.map +1 -0
- package/dist/index.browser.d.ts +6 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/io/index.d.ts +2 -0
- package/dist/io/index.d.ts.map +1 -0
- package/dist/io/loader.node.d.ts +14 -0
- package/dist/io/loader.node.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +6 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/completions.js +624 -0
- package/dist/lsp/completions.js.map +1 -0
- package/dist/lsp/data-context.d.ts +60 -0
- package/dist/lsp/data-context.d.ts.map +1 -0
- package/dist/lsp/data-context.js +172 -0
- package/dist/lsp/data-context.js.map +1 -0
- package/dist/lsp/diagnostics.d.ts +7 -0
- package/dist/lsp/diagnostics.d.ts.map +1 -0
- package/dist/lsp/diagnostics.js +373 -0
- package/dist/lsp/diagnostics.js.map +1 -0
- package/dist/lsp/directives.d.ts +44 -0
- package/dist/lsp/directives.d.ts.map +1 -0
- package/dist/lsp/directives.js +232 -0
- package/dist/lsp/directives.js.map +1 -0
- package/dist/lsp/domain/index.d.ts +2 -0
- package/dist/lsp/domain/index.d.ts.map +1 -0
- package/dist/lsp/domain/index.js +18 -0
- package/dist/lsp/domain/index.js.map +1 -0
- package/dist/lsp/domain/validator.d.ts +2433 -0
- package/dist/lsp/domain/validator.d.ts.map +1 -0
- package/dist/lsp/domain/validator.js +225 -0
- package/dist/lsp/domain/validator.js.map +1 -0
- package/dist/lsp/hover.d.ts +7 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/hover.js +462 -0
- package/dist/lsp/hover.js.map +1 -0
- package/dist/lsp/lsp/completions.d.ts +6 -0
- package/dist/lsp/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/lsp/completions.js +624 -0
- package/dist/lsp/lsp/completions.js.map +1 -0
- package/dist/lsp/lsp/data-context.d.ts +60 -0
- package/dist/lsp/lsp/data-context.d.ts.map +1 -0
- package/dist/lsp/lsp/data-context.js +172 -0
- package/dist/lsp/lsp/data-context.js.map +1 -0
- package/dist/lsp/lsp/diagnostics.d.ts +7 -0
- package/dist/lsp/lsp/diagnostics.d.ts.map +1 -0
- package/dist/lsp/lsp/diagnostics.js +373 -0
- package/dist/lsp/lsp/diagnostics.js.map +1 -0
- package/dist/lsp/lsp/directives.d.ts +44 -0
- package/dist/lsp/lsp/directives.d.ts.map +1 -0
- package/dist/lsp/lsp/directives.js +232 -0
- package/dist/lsp/lsp/directives.js.map +1 -0
- package/dist/lsp/lsp/hover.d.ts +7 -0
- package/dist/lsp/lsp/hover.d.ts.map +1 -0
- package/dist/lsp/lsp/hover.js +462 -0
- package/dist/lsp/lsp/hover.js.map +1 -0
- package/dist/lsp/lsp/semantic_tokens.d.ts +4 -0
- package/dist/lsp/lsp/semantic_tokens.d.ts.map +1 -0
- package/dist/lsp/lsp/semantic_tokens.js +158 -0
- package/dist/lsp/lsp/semantic_tokens.js.map +1 -0
- package/dist/lsp/lsp/server.d.ts +2 -0
- package/dist/lsp/lsp/server.d.ts.map +1 -0
- package/dist/lsp/lsp/server.js +216 -0
- package/dist/lsp/lsp/server.js.map +1 -0
- package/dist/lsp/semantic_tokens.d.ts +4 -0
- package/dist/lsp/semantic_tokens.d.ts.map +1 -0
- package/dist/lsp/semantic_tokens.js +158 -0
- package/dist/lsp/semantic_tokens.js.map +1 -0
- package/dist/lsp/server.bundle.d.ts +2 -0
- package/dist/lsp/server.bundle.d.ts.map +1 -0
- package/dist/lsp/server.bundle.js +256 -0
- package/dist/lsp/server.d.ts +2 -0
- package/dist/lsp/server.d.ts.map +1 -0
- package/dist/lsp/server.js +216 -0
- package/dist/lsp/server.js.map +1 -0
- package/dist/lsp/types.d.ts +71 -0
- package/dist/lsp/types.d.ts.map +1 -0
- package/dist/lsp/types.js +4 -0
- package/dist/lsp/types.js.map +1 -0
- package/dist/node/index.js +186 -0
- package/dist/node/index.js.map +196 -0
- package/dist/node/node.js +187 -0
- package/dist/node/node.js.map +198 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/sdk/builder.d.ts +39 -0
- package/dist/sdk/builder.d.ts.map +1 -0
- package/dist/sdk/exporter.d.ts +13 -0
- package/dist/sdk/exporter.d.ts.map +1 -0
- package/dist/sdk/index.d.ts +3 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/emitter.d.ts +25 -0
- package/dist/utils/emitter.d.ts.map +1 -0
- package/dist/utils/utils.d.ts +18 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCompletionItems = getCompletionItems;
|
|
4
|
+
exports.findPathAtOffset = findPathAtOffset;
|
|
5
|
+
const node_1 = require("vscode-languageserver/node");
|
|
6
|
+
const yaml_1 = require("yaml");
|
|
7
|
+
const data_context_1 = require("./data-context");
|
|
8
|
+
const directives_1 = require("./directives");
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
// --- CONSTANTS & DEFINITIONS ---
|
|
12
|
+
const TOP_LEVEL_KEYS = [
|
|
13
|
+
{ label: 'id', kind: node_1.CompletionItemKind.Field, detail: 'Unique identifier for the rule' },
|
|
14
|
+
{ label: 'name', kind: node_1.CompletionItemKind.Field, detail: 'Human readable name' },
|
|
15
|
+
{ label: 'description', kind: node_1.CompletionItemKind.Field, detail: 'What this rule does' },
|
|
16
|
+
{ label: 'on', kind: node_1.CompletionItemKind.Keyword, detail: 'The event that triggers this rule' },
|
|
17
|
+
{ label: 'if', kind: node_1.CompletionItemKind.Keyword, detail: 'Conditions that must be met' },
|
|
18
|
+
{ label: 'do', kind: node_1.CompletionItemKind.Keyword, detail: 'Actions to perform when triggered' },
|
|
19
|
+
{ label: 'priority', kind: node_1.CompletionItemKind.Property, detail: 'Rule execution priority (higher = first)' },
|
|
20
|
+
{ label: 'enabled', kind: node_1.CompletionItemKind.Property, detail: 'Whether this rule is active' },
|
|
21
|
+
{ label: 'cooldown', kind: node_1.CompletionItemKind.Property, detail: 'Wait time in ms between executions' },
|
|
22
|
+
{ label: 'tags', kind: node_1.CompletionItemKind.Property, detail: 'Categorization tags' },
|
|
23
|
+
{ label: 'comment', kind: node_1.CompletionItemKind.Text, detail: 'Internal developer note' }
|
|
24
|
+
];
|
|
25
|
+
const EVENTS = [
|
|
26
|
+
{ label: 'minecraft:player_join', kind: node_1.CompletionItemKind.Event },
|
|
27
|
+
{ label: 'minecraft:player_quit', kind: node_1.CompletionItemKind.Event },
|
|
28
|
+
{ label: 'minecraft:chat', kind: node_1.CompletionItemKind.Event },
|
|
29
|
+
{ label: 'tiktok:chat', kind: node_1.CompletionItemKind.Event },
|
|
30
|
+
{ label: 'tiktok:gift', kind: node_1.CompletionItemKind.Event },
|
|
31
|
+
{ label: 'tiktok:like', kind: node_1.CompletionItemKind.Event },
|
|
32
|
+
{ label: 'twitch:chat', kind: node_1.CompletionItemKind.Event },
|
|
33
|
+
{ label: 'twitch:follow', kind: node_1.CompletionItemKind.Event },
|
|
34
|
+
{ label: 'bopl:webhook', kind: node_1.CompletionItemKind.Event },
|
|
35
|
+
{ label: 'ANY_EVENT', kind: node_1.CompletionItemKind.Event },
|
|
36
|
+
{ label: 'USER_LOGIN', kind: node_1.CompletionItemKind.Event },
|
|
37
|
+
{ label: 'GAME_OVER', kind: node_1.CompletionItemKind.Event },
|
|
38
|
+
{ label: 'COMMAND', kind: node_1.CompletionItemKind.Event },
|
|
39
|
+
{ label: 'ALERT', kind: node_1.CompletionItemKind.Event }
|
|
40
|
+
];
|
|
41
|
+
const OPERATORS = [
|
|
42
|
+
{ label: 'EQ', kind: node_1.CompletionItemKind.Operator, detail: 'Equal (==)' },
|
|
43
|
+
{ label: 'NEQ', kind: node_1.CompletionItemKind.Operator, detail: 'Not Equal (!=)' },
|
|
44
|
+
{ label: 'GT', kind: node_1.CompletionItemKind.Operator, detail: 'Greater Than (>)' },
|
|
45
|
+
{ label: 'GTE', kind: node_1.CompletionItemKind.Operator, detail: 'Greater Than Equals (>=)' },
|
|
46
|
+
{ label: 'LT', kind: node_1.CompletionItemKind.Operator, detail: 'Less Than (<)' },
|
|
47
|
+
{ label: 'LTE', kind: node_1.CompletionItemKind.Operator, detail: 'Less Than Equals (<=)' },
|
|
48
|
+
{ label: 'IN', kind: node_1.CompletionItemKind.Operator, detail: 'Value exists in the provided list' },
|
|
49
|
+
{ label: 'NOT_IN', kind: node_1.CompletionItemKind.Operator, detail: 'Value does not exist in the list' },
|
|
50
|
+
{ label: 'CONTAINS', kind: node_1.CompletionItemKind.Operator, detail: 'String contains substring or List contains item' },
|
|
51
|
+
{ label: 'MATCHES', kind: node_1.CompletionItemKind.Operator, detail: 'Regex pattern match' },
|
|
52
|
+
{ label: 'RANGE', kind: node_1.CompletionItemKind.Operator, detail: 'Numeric value between [min, max]' },
|
|
53
|
+
{ label: 'SINCE', kind: node_1.CompletionItemKind.Operator, detail: 'Date is after or equal to value' },
|
|
54
|
+
{ label: 'AFTER', kind: node_1.CompletionItemKind.Operator, detail: 'Alias for SINCE' },
|
|
55
|
+
{ label: 'BEFORE', kind: node_1.CompletionItemKind.Operator, detail: 'Date is before value' },
|
|
56
|
+
{ label: 'UNTIL', kind: node_1.CompletionItemKind.Operator, detail: 'Alias for BEFORE' },
|
|
57
|
+
{ label: 'AND', kind: node_1.CompletionItemKind.Operator, detail: 'Logical AND (for groups)' },
|
|
58
|
+
{ label: 'OR', kind: node_1.CompletionItemKind.Operator, detail: 'Logical OR (for groups)' }
|
|
59
|
+
];
|
|
60
|
+
const ACTION_TYPES = [
|
|
61
|
+
{ label: 'log', kind: node_1.CompletionItemKind.EnumMember, detail: 'Print message to console' },
|
|
62
|
+
{ label: 'execute', kind: node_1.CompletionItemKind.EnumMember, detail: 'Run local command' },
|
|
63
|
+
{ label: 'forward', kind: node_1.CompletionItemKind.EnumMember, detail: 'Forward event to URL' },
|
|
64
|
+
{ label: 'response', kind: node_1.CompletionItemKind.EnumMember, detail: 'Return HTTP response' },
|
|
65
|
+
{ label: 'STATE_SET', kind: node_1.CompletionItemKind.EnumMember, detail: 'Save value to global state' },
|
|
66
|
+
{ label: 'STATE_INCREMENT', kind: node_1.CompletionItemKind.EnumMember, detail: 'Increment numeric state key' },
|
|
67
|
+
{ label: 'EMIT_EVENT', kind: node_1.CompletionItemKind.EnumMember, detail: 'Trigger another event internally' },
|
|
68
|
+
];
|
|
69
|
+
const CONDITION_KEYS = [
|
|
70
|
+
{ label: 'field', kind: node_1.CompletionItemKind.Field, detail: 'Path to context data (e.g. data.user)' },
|
|
71
|
+
{ label: 'operator', kind: node_1.CompletionItemKind.Field, detail: 'Comparison operator (EQ, GT, etc.)' },
|
|
72
|
+
{ label: 'value', kind: node_1.CompletionItemKind.Value, detail: 'The value to compare against' },
|
|
73
|
+
{ label: 'conditions', kind: node_1.CompletionItemKind.Field, detail: 'Sub-conditions for grouping' }
|
|
74
|
+
];
|
|
75
|
+
const ACTION_KEYS = [
|
|
76
|
+
{ label: 'type', kind: node_1.CompletionItemKind.Field, detail: 'The type of action to perform' },
|
|
77
|
+
{ label: 'params', kind: node_1.CompletionItemKind.Variable, detail: 'Configuration for the action' },
|
|
78
|
+
{ label: 'delay', kind: node_1.CompletionItemKind.Property, detail: 'Delay in ms (integer)' },
|
|
79
|
+
{ label: 'probability', kind: node_1.CompletionItemKind.Property, detail: 'Execution chance (0-1)' },
|
|
80
|
+
{ label: 'mode', kind: node_1.CompletionItemKind.Property, detail: 'Grouping mode (ALL, SEQUENCE, EITHER)' },
|
|
81
|
+
{ label: 'actions', kind: node_1.CompletionItemKind.Property, detail: 'List of sub-actions' }
|
|
82
|
+
];
|
|
83
|
+
const PARAM_KEYS = {
|
|
84
|
+
'log': [
|
|
85
|
+
{ label: 'message', kind: node_1.CompletionItemKind.Property },
|
|
86
|
+
{ label: 'content', kind: node_1.CompletionItemKind.Property },
|
|
87
|
+
{ label: 'level', kind: node_1.CompletionItemKind.Property, detail: 'info, warn, error' },
|
|
88
|
+
],
|
|
89
|
+
'execute': [
|
|
90
|
+
{ label: 'command', kind: node_1.CompletionItemKind.Property },
|
|
91
|
+
{ label: 'safe', kind: node_1.CompletionItemKind.Property, detail: 'boolean (default: false)' },
|
|
92
|
+
{ label: 'dir', kind: node_1.CompletionItemKind.Property, detail: 'working directory' },
|
|
93
|
+
],
|
|
94
|
+
'forward': [
|
|
95
|
+
{ label: 'url', kind: node_1.CompletionItemKind.Property },
|
|
96
|
+
{ label: 'method', kind: node_1.CompletionItemKind.Property, detail: 'POST, GET, PUT...' },
|
|
97
|
+
{ label: 'headers', kind: node_1.CompletionItemKind.Property },
|
|
98
|
+
{ label: 'body', kind: node_1.CompletionItemKind.Property },
|
|
99
|
+
],
|
|
100
|
+
'response': [
|
|
101
|
+
{ label: 'content', kind: node_1.CompletionItemKind.Property },
|
|
102
|
+
{ label: 'statusCode', kind: node_1.CompletionItemKind.Property, detail: '200, 404, etc.' },
|
|
103
|
+
{ label: 'contentType', kind: node_1.CompletionItemKind.Property, detail: 'application/json' },
|
|
104
|
+
],
|
|
105
|
+
'STATE_SET': [
|
|
106
|
+
{ label: 'key', kind: node_1.CompletionItemKind.Property },
|
|
107
|
+
{ label: 'value', kind: node_1.CompletionItemKind.Property },
|
|
108
|
+
{ label: 'ttl', kind: node_1.CompletionItemKind.Property, detail: 'Time to live in ms' },
|
|
109
|
+
],
|
|
110
|
+
'STATE_INCREMENT': [
|
|
111
|
+
{ label: 'key', kind: node_1.CompletionItemKind.Property },
|
|
112
|
+
{ label: 'amount', kind: node_1.CompletionItemKind.Property },
|
|
113
|
+
],
|
|
114
|
+
'EMIT_EVENT': [
|
|
115
|
+
{ label: 'event', kind: node_1.CompletionItemKind.Property },
|
|
116
|
+
{ label: 'data', kind: node_1.CompletionItemKind.Property },
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
const SNIPPETS = [
|
|
120
|
+
{
|
|
121
|
+
label: 'trigger_rule',
|
|
122
|
+
kind: node_1.CompletionItemKind.Snippet,
|
|
123
|
+
insertText: '- id: ${1:rule-id}\n on: ${2:EVENT}\n if:\n field: ${3:data.field}\n operator: ${4:EQ}\n value: ${5:target}\n do:\n type: ${6:log}\n params:\n message: ${7:Done}',
|
|
124
|
+
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
125
|
+
detail: 'New rule template'
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
label: 'log_action',
|
|
129
|
+
kind: node_1.CompletionItemKind.Snippet,
|
|
130
|
+
insertText: 'type: log\nparams:\n message: ${1:message}',
|
|
131
|
+
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
132
|
+
detail: 'Log action template'
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
label: 'condition_nested',
|
|
136
|
+
kind: node_1.CompletionItemKind.Snippet,
|
|
137
|
+
insertText: 'operator: ${1|AND,OR|}\nconditions:\n - field: ${2:data.x}\n operator: ${3:EQ}\n value: ${4:val}',
|
|
138
|
+
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
139
|
+
detail: 'Nested condition group'
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
// --- MAIN LOGIC ---
|
|
143
|
+
function getCompletionItems(document, position) {
|
|
144
|
+
console.log(`[LSP] getCompletionItems called for document: ${document.uri}`);
|
|
145
|
+
console.log(`[LSP] Position: line ${position.line}, character ${position.character}`);
|
|
146
|
+
// Load data from import directives only (declarative approach)
|
|
147
|
+
const imports = (0, directives_1.getImportDirectives)(document, document.uri);
|
|
148
|
+
console.log(`[LSP] Found ${imports.length} import directives`);
|
|
149
|
+
if (imports.length > 0) {
|
|
150
|
+
console.log(`[LSP] Loading data from imports:`, imports);
|
|
151
|
+
(0, data_context_1.loadDataFromImports)(imports);
|
|
152
|
+
// Verificar datos cargados
|
|
153
|
+
const allData = data_context_1.globalDataContext.getValue('');
|
|
154
|
+
console.log(`[LSP] Data loaded in context:`, allData);
|
|
155
|
+
console.log(`[LSP] Available top-level keys:`, allData ? Object.keys(allData) : 'none');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log(`[LSP] No imports found, clearing data context`);
|
|
159
|
+
// Clear data context when no imports are defined
|
|
160
|
+
data_context_1.globalDataContext.clear();
|
|
161
|
+
}
|
|
162
|
+
const text = document.getText();
|
|
163
|
+
const doc = (0, yaml_1.parseDocument)(text);
|
|
164
|
+
const lines = text.split('\n');
|
|
165
|
+
const line = lines[position.line] || '';
|
|
166
|
+
const offset = document.offsetAt(position);
|
|
167
|
+
console.log(`[LSP] Current line: "${line}"`);
|
|
168
|
+
console.log(`[LSP] Offset: ${offset}`);
|
|
169
|
+
// Check if we're in a comment line (for directive completion)
|
|
170
|
+
if (line.trim().startsWith('#')) {
|
|
171
|
+
const directiveCompletions = getDirectiveCompletions(line, position.character, document);
|
|
172
|
+
if (directiveCompletions.length > 0) {
|
|
173
|
+
return directiveCompletions;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Check if we're inside a template variable ${...}
|
|
177
|
+
const templateMatch = checkTemplateVariable(line, position.character);
|
|
178
|
+
if (templateMatch) {
|
|
179
|
+
return getTemplateVariableCompletions(templateMatch);
|
|
180
|
+
}
|
|
181
|
+
// 1. Check if we are in a VALUE position (after colon)
|
|
182
|
+
const colonIndex = line.indexOf(':');
|
|
183
|
+
if (colonIndex !== -1 && position.character > colonIndex) {
|
|
184
|
+
const key = line.substring(0, colonIndex).trim().replace(/^- /, '');
|
|
185
|
+
const path = findPathAtOffset(doc.contents, offset) || [];
|
|
186
|
+
return getValueCompletionsByKey(key, path);
|
|
187
|
+
}
|
|
188
|
+
// 2. We are in a KEY position or start of line
|
|
189
|
+
const path = findPathAtOffset(doc.contents, offset) || [];
|
|
190
|
+
const completions = getKeyCompletions(path, line);
|
|
191
|
+
console.log(`[LSP] Returning ${completions.length} completion items`);
|
|
192
|
+
if (completions.length > 0) {
|
|
193
|
+
console.log(`[LSP] First few items:`, completions.slice(0, 3).map(c => c.label));
|
|
194
|
+
}
|
|
195
|
+
return completions;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Check if cursor is inside a template variable and return the context
|
|
199
|
+
*/
|
|
200
|
+
function checkTemplateVariable(line, character) {
|
|
201
|
+
console.log(`[LSP] checkTemplateVariable - line: "${line}", character: ${character}`);
|
|
202
|
+
// Find all template variable positions in the line
|
|
203
|
+
const regex = /\$\{([^}]*)\}/g;
|
|
204
|
+
let match;
|
|
205
|
+
while ((match = regex.exec(line)) !== null) {
|
|
206
|
+
const start = match.index;
|
|
207
|
+
const end = match.index + match[0].length;
|
|
208
|
+
console.log(`[LSP] Found template: "${match[0]}" at positions ${start}-${end}`);
|
|
209
|
+
// Check if cursor is inside this template (inclusive of start and end)
|
|
210
|
+
if (character >= start && character <= end) {
|
|
211
|
+
const content = match[1] || '';
|
|
212
|
+
const dotIndex = content.lastIndexOf('.');
|
|
213
|
+
console.log(`[LSP] Cursor inside template, content: "${content}", dotIndex: ${dotIndex}`);
|
|
214
|
+
return {
|
|
215
|
+
prefix: dotIndex >= 0 ? content.substring(0, dotIndex + 1) : content,
|
|
216
|
+
inTemplate: true
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Check if we're typing after ${
|
|
221
|
+
const beforeCursor = line.substring(0, character);
|
|
222
|
+
const lastDollarBrace = beforeCursor.lastIndexOf('${');
|
|
223
|
+
const lastCloseBrace = beforeCursor.lastIndexOf('}');
|
|
224
|
+
console.log(`[LSP] Checking for incomplete template - lastDollarBrace: ${lastDollarBrace}, lastCloseBrace: ${lastCloseBrace}`);
|
|
225
|
+
if (lastDollarBrace > lastCloseBrace) {
|
|
226
|
+
const content = beforeCursor.substring(lastDollarBrace + 2);
|
|
227
|
+
const dotIndex = content.lastIndexOf('.');
|
|
228
|
+
console.log(`[LSP] Found incomplete template, content: "${content}", dotIndex: ${dotIndex}`);
|
|
229
|
+
return {
|
|
230
|
+
prefix: dotIndex >= 0 ? content.substring(0, dotIndex + 1) : content,
|
|
231
|
+
inTemplate: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// Check if we're right at the $ or { position and should start a new template
|
|
235
|
+
if (character > 0) {
|
|
236
|
+
const charBefore = line[character - 1];
|
|
237
|
+
if (charBefore === '$' || charBefore === '{') {
|
|
238
|
+
console.log(`[LSP] Found $ or { at position ${character - 1}`);
|
|
239
|
+
return {
|
|
240
|
+
prefix: '',
|
|
241
|
+
inTemplate: true
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Special check: if we're at the very beginning of a potential template
|
|
246
|
+
if (character < line.length) {
|
|
247
|
+
const remaining = line.substring(character);
|
|
248
|
+
if (remaining.startsWith('${') || remaining.startsWith('{')) {
|
|
249
|
+
console.log(`[LSP] Found potential template start at current position`);
|
|
250
|
+
return {
|
|
251
|
+
prefix: '',
|
|
252
|
+
inTemplate: true
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
console.log(`[LSP] No template found`);
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get completions for template variables
|
|
261
|
+
*/
|
|
262
|
+
function getTemplateVariableCompletions(context) {
|
|
263
|
+
const prefix = context.prefix.trim();
|
|
264
|
+
console.log(`[LSP] Template variable completion - prefix: "${prefix}"`);
|
|
265
|
+
// Handle different variable types based on what's loaded in globalDataContext
|
|
266
|
+
const allData = data_context_1.globalDataContext.getValue('');
|
|
267
|
+
console.log(`[LSP] Current data context:`, allData);
|
|
268
|
+
if (!allData || typeof allData !== 'object') {
|
|
269
|
+
console.log(`[LSP] No data available in context`);
|
|
270
|
+
return [{
|
|
271
|
+
label: 'data',
|
|
272
|
+
kind: node_1.CompletionItemKind.Variable,
|
|
273
|
+
detail: 'No imported data available',
|
|
274
|
+
documentation: 'Add a data import directive like: # @import data from ./data.json'
|
|
275
|
+
}];
|
|
276
|
+
}
|
|
277
|
+
// Check if we're at the root level (after ${ or ${data. etc)
|
|
278
|
+
const cleanPrefix = prefix.replace('${', '').replace(/\.$/, '');
|
|
279
|
+
console.log(`[LSP] Clean prefix: "${cleanPrefix}"`);
|
|
280
|
+
// If we're at root level, suggest all available top-level variables (aliases)
|
|
281
|
+
if (!cleanPrefix || cleanPrefix === '') {
|
|
282
|
+
const suggestions = Object.keys(allData).map(key => {
|
|
283
|
+
const value = allData[key];
|
|
284
|
+
const valueType = Array.isArray(value) ? 'array' : typeof value;
|
|
285
|
+
const sampleValue = valueType === 'object' ? JSON.stringify(value).substring(0, 50) + '...' : String(value);
|
|
286
|
+
return {
|
|
287
|
+
label: key,
|
|
288
|
+
kind: node_1.CompletionItemKind.Variable,
|
|
289
|
+
detail: `${valueType} (imported data)`,
|
|
290
|
+
documentation: `Sample value: ${sampleValue}`,
|
|
291
|
+
insertText: key
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
console.log(`[LSP] Root level suggestions:`, suggestions.map(s => s.label));
|
|
295
|
+
return suggestions;
|
|
296
|
+
}
|
|
297
|
+
// If we have a specific prefix, get fields from that path
|
|
298
|
+
// The prefix might be something like "data" or "data.server" or "config.username"
|
|
299
|
+
const fields = data_context_1.globalDataContext.getFields(cleanPrefix);
|
|
300
|
+
console.log(`[LSP] Fields for prefix "${cleanPrefix}":`, fields.map(f => f.name));
|
|
301
|
+
if (fields.length > 0) {
|
|
302
|
+
const suggestions = fields.map(field => {
|
|
303
|
+
const sampleValue = field.type === 'object' ?
|
|
304
|
+
JSON.stringify(field.value).substring(0, 50) + '...' :
|
|
305
|
+
data_context_1.globalDataContext.getFormattedValue(field.value);
|
|
306
|
+
return {
|
|
307
|
+
label: field.name,
|
|
308
|
+
kind: field.type === 'object' ? node_1.CompletionItemKind.Module : node_1.CompletionItemKind.Field,
|
|
309
|
+
detail: `${field.type} (imported data)`,
|
|
310
|
+
documentation: `Sample value: ${sampleValue}`,
|
|
311
|
+
insertText: field.name
|
|
312
|
+
};
|
|
313
|
+
});
|
|
314
|
+
console.log(`[LSP] Field suggestions:`, suggestions.map(s => s.label));
|
|
315
|
+
return suggestions;
|
|
316
|
+
}
|
|
317
|
+
// If no fields found, suggest similar paths or provide helpful message
|
|
318
|
+
console.log(`[LSP] No suggestions found for prefix "${prefix}"`);
|
|
319
|
+
// Check if we have any data at all to provide suggestions
|
|
320
|
+
const allKeys = Object.keys(allData);
|
|
321
|
+
if (allKeys.length > 0) {
|
|
322
|
+
return [{
|
|
323
|
+
label: cleanPrefix,
|
|
324
|
+
kind: node_1.CompletionItemKind.Text,
|
|
325
|
+
detail: 'Path not found in imported data',
|
|
326
|
+
documentation: `Available top-level keys: ${allKeys.join(', ')}`,
|
|
327
|
+
insertText: cleanPrefix
|
|
328
|
+
}];
|
|
329
|
+
}
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
function getValueCompletionsByKey(key, path) {
|
|
333
|
+
switch (key) {
|
|
334
|
+
case 'on': return EVENTS;
|
|
335
|
+
case 'operator': return OPERATORS;
|
|
336
|
+
case 'type': return ACTION_TYPES;
|
|
337
|
+
case 'mode':
|
|
338
|
+
return [
|
|
339
|
+
{ label: 'ALL', kind: node_1.CompletionItemKind.EnumMember, detail: 'Execute all' },
|
|
340
|
+
{ label: 'SEQUENCE', kind: node_1.CompletionItemKind.EnumMember, detail: 'Wait for each' },
|
|
341
|
+
{ label: 'EITHER', kind: node_1.CompletionItemKind.EnumMember, detail: 'Random choice' }
|
|
342
|
+
];
|
|
343
|
+
case 'enabled':
|
|
344
|
+
return [
|
|
345
|
+
{ label: 'true', kind: node_1.CompletionItemKind.Value },
|
|
346
|
+
{ label: 'false', kind: node_1.CompletionItemKind.Value }
|
|
347
|
+
];
|
|
348
|
+
case 'field':
|
|
349
|
+
// Only suggest imported data fields
|
|
350
|
+
const fields = data_context_1.globalDataContext.getFields('data');
|
|
351
|
+
return fields.map(field => ({
|
|
352
|
+
label: field.name,
|
|
353
|
+
kind: node_1.CompletionItemKind.Field,
|
|
354
|
+
detail: `${field.type}${field.value !== undefined ? ` = ${data_context_1.globalDataContext.getFormattedValue(field.value)}` : ''}`
|
|
355
|
+
}));
|
|
356
|
+
case 'value':
|
|
357
|
+
return getValueSpecificToOperator(path);
|
|
358
|
+
}
|
|
359
|
+
// Return empty array instead of DYNAMIC_VALUES
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
function getKeyCompletions(path, line) {
|
|
363
|
+
// If line starts with '- ', we might be in a list
|
|
364
|
+
if (line.trim().startsWith('-')) {
|
|
365
|
+
const parentPair = findEffectiveParentPair(path);
|
|
366
|
+
if (parentPair) {
|
|
367
|
+
const pk = String(parentPair.key.value);
|
|
368
|
+
if (pk === 'do' || pk === 'actions')
|
|
369
|
+
return ACTION_KEYS;
|
|
370
|
+
if (pk === 'if' || pk === 'conditions')
|
|
371
|
+
return CONDITION_KEYS;
|
|
372
|
+
}
|
|
373
|
+
return SNIPPETS.length > 0 ? [SNIPPETS[0]] : [];
|
|
374
|
+
}
|
|
375
|
+
const contextPair = findEffectiveParentPair(path);
|
|
376
|
+
if (!contextPair)
|
|
377
|
+
return TOP_LEVEL_KEYS;
|
|
378
|
+
const key = String(contextPair.key.value);
|
|
379
|
+
if (key === 'if' || key === 'conditions')
|
|
380
|
+
return CONDITION_KEYS;
|
|
381
|
+
if (key === 'do' || key === 'actions')
|
|
382
|
+
return ACTION_KEYS;
|
|
383
|
+
if (key === 'params') {
|
|
384
|
+
const actionMap = findNearestActionMap(path);
|
|
385
|
+
if (actionMap) {
|
|
386
|
+
const typePair = actionMap.items.find(item => (0, yaml_1.isPair)(item) && String(item.key.value) === 'type');
|
|
387
|
+
if (typePair && (0, yaml_1.isScalar)(typePair.value)) {
|
|
388
|
+
return PARAM_KEYS[String(typePair.value.value)] || [];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return TOP_LEVEL_KEYS;
|
|
393
|
+
}
|
|
394
|
+
// --- HELPERS ---
|
|
395
|
+
function isKeyOfParent(node, path) {
|
|
396
|
+
const parent = path[path.length - 2];
|
|
397
|
+
if ((0, yaml_1.isPair)(parent))
|
|
398
|
+
return parent.key === node;
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
function findEffectiveParentPair(path) {
|
|
402
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
403
|
+
const item = path[i];
|
|
404
|
+
if ((0, yaml_1.isPair)(item))
|
|
405
|
+
return item;
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
function findNearestActionMap(path) {
|
|
410
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
411
|
+
const item = path[i];
|
|
412
|
+
if ((0, yaml_1.isMap)(item)) {
|
|
413
|
+
const hasType = item.items.some(p => (0, yaml_1.isPair)(p) && String(p.key.value) === 'type');
|
|
414
|
+
if (hasType)
|
|
415
|
+
return item;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
function getValueSpecificToOperator(path) {
|
|
421
|
+
// Look for a map in the path that contains an 'operator' key
|
|
422
|
+
const map = path.slice().reverse().find(n => (0, yaml_1.isMap)(n));
|
|
423
|
+
if (!map)
|
|
424
|
+
return [];
|
|
425
|
+
const opPair = map.items.find(item => (0, yaml_1.isPair)(item) && String(item.key.value) === 'operator');
|
|
426
|
+
if (!opPair || !(0, yaml_1.isScalar)(opPair.value))
|
|
427
|
+
return [];
|
|
428
|
+
const op = String(opPair.value.value);
|
|
429
|
+
switch (op) {
|
|
430
|
+
case 'RANGE':
|
|
431
|
+
return [{ label: '[min, max]', kind: node_1.CompletionItemKind.Snippet, insertText: '[$1, $2]', insertTextFormat: node_1.InsertTextFormat.Snippet }];
|
|
432
|
+
case 'IN':
|
|
433
|
+
case 'NOT_IN':
|
|
434
|
+
return [{ label: '[item1, item2]', kind: node_1.CompletionItemKind.Snippet, insertText: '[$1, $2]', insertTextFormat: node_1.InsertTextFormat.Snippet }];
|
|
435
|
+
case 'MATCHES':
|
|
436
|
+
return [{ label: '"regex"', kind: node_1.CompletionItemKind.Snippet, insertText: '"^$1$"', insertTextFormat: node_1.InsertTextFormat.Snippet }];
|
|
437
|
+
}
|
|
438
|
+
return [];
|
|
439
|
+
}
|
|
440
|
+
function findPathAtOffset(node, offset, currentPath = []) {
|
|
441
|
+
if (!node)
|
|
442
|
+
return null;
|
|
443
|
+
// Check range
|
|
444
|
+
const range = node.range;
|
|
445
|
+
if (range) {
|
|
446
|
+
// [start, end, optional_something]
|
|
447
|
+
// Parser range is [start, end].
|
|
448
|
+
// We want to be inclusive and a bit more for completions at the end of a line.
|
|
449
|
+
if (offset < range[0] || offset > range[1] + 1) {
|
|
450
|
+
// If we are exactly 1 char past the end (like at the end of "mode: "),
|
|
451
|
+
// we still might want this node if it's the most specific one.
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const newPath = [...currentPath, node];
|
|
455
|
+
if ((0, yaml_1.isMap)(node)) {
|
|
456
|
+
for (const item of node.items) {
|
|
457
|
+
if ((0, yaml_1.isPair)(item)) {
|
|
458
|
+
const itemRange = item.range;
|
|
459
|
+
if (itemRange && offset >= itemRange[0] && offset <= itemRange[1] + 1) {
|
|
460
|
+
return findPathAtOffset(item, offset, newPath);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return newPath;
|
|
465
|
+
}
|
|
466
|
+
if ((0, yaml_1.isSeq)(node)) {
|
|
467
|
+
for (const item of node.items) {
|
|
468
|
+
const itemRange = item.range;
|
|
469
|
+
if (itemRange && offset >= itemRange[0] && offset <= itemRange[1] + 1) {
|
|
470
|
+
return findPathAtOffset(item, offset, newPath);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return newPath;
|
|
474
|
+
}
|
|
475
|
+
if ((0, yaml_1.isPair)(node)) {
|
|
476
|
+
// If we are in a pair, we could be in key or value
|
|
477
|
+
const keyRange = node.key?.range;
|
|
478
|
+
if (keyRange && offset >= keyRange[0] && offset <= keyRange[1] + 1) {
|
|
479
|
+
return findPathAtOffset(node.key, offset, newPath);
|
|
480
|
+
}
|
|
481
|
+
// If there's a value, check it
|
|
482
|
+
if (node.value) {
|
|
483
|
+
const valRange = node.value?.range;
|
|
484
|
+
if (valRange && offset >= valRange[0] && offset <= valRange[1] + 1) {
|
|
485
|
+
return findPathAtOffset(node.value, offset, newPath);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return newPath;
|
|
489
|
+
}
|
|
490
|
+
return newPath;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Get completions for directive comments (lines starting with #)
|
|
494
|
+
*/
|
|
495
|
+
function getDirectiveCompletions(line, character, document) {
|
|
496
|
+
console.log(`[LSP] Checking directive completions for line: "${line}" at character ${character}`);
|
|
497
|
+
// Check if we're in a directive context
|
|
498
|
+
const directiveMatch = line.match(/#\s*@?([\w-]*)$/);
|
|
499
|
+
if (!directiveMatch)
|
|
500
|
+
return [];
|
|
501
|
+
const partialDirective = directiveMatch[1] || '';
|
|
502
|
+
console.log(`[LSP] Partial directive: "${partialDirective}"`);
|
|
503
|
+
// Check if we're in an import directive and need file path completion
|
|
504
|
+
if (partialDirective.startsWith('import') || line.includes('@import')) {
|
|
505
|
+
const importMatch = line.match(/@import\s+\w+\s+from\s+['"]?([^'"]*)$/);
|
|
506
|
+
if (importMatch) {
|
|
507
|
+
const partialPath = importMatch[1] || '';
|
|
508
|
+
return getImportFileCompletions(document.uri, partialPath);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// If we're just starting a directive (after # or @)
|
|
512
|
+
if (partialDirective === '' || line.match(/#\s*$/)) {
|
|
513
|
+
return getAllDirectiveCompletions();
|
|
514
|
+
}
|
|
515
|
+
// If we have a partial directive, filter completions
|
|
516
|
+
const allDirectives = getAllDirectiveCompletions();
|
|
517
|
+
return allDirectives.filter(item => item.label.toLowerCase().startsWith(partialDirective.toLowerCase()));
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Get all available directive completions with enhanced categorization and colors
|
|
521
|
+
*/
|
|
522
|
+
function getAllDirectiveCompletions() {
|
|
523
|
+
const directives = [
|
|
524
|
+
// Import directives (Macro category - distinctive color)
|
|
525
|
+
{
|
|
526
|
+
label: 'import',
|
|
527
|
+
kind: node_1.CompletionItemKind.Function, // Function type for imports
|
|
528
|
+
detail: 'Import data from JSON/YAML file',
|
|
529
|
+
insertText: 'import ${1:alias} from ${2:./path/to/file.json}',
|
|
530
|
+
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
531
|
+
documentation: 'Imports data from a JSON or YAML file for use in autocompletion and validation. Example: @import data from ./data.json',
|
|
532
|
+
data: { category: 'import', color: 'macro' }
|
|
533
|
+
},
|
|
534
|
+
// Global lint control (Namespace category)
|
|
535
|
+
{
|
|
536
|
+
label: 'disable-lint',
|
|
537
|
+
kind: node_1.CompletionItemKind.Module, // Module type for global controls
|
|
538
|
+
detail: 'Disable all linting for subsequent lines',
|
|
539
|
+
insertText: 'disable-lint',
|
|
540
|
+
documentation: 'Disables all linting and validation for the rest of the document or until @enable-lint is encountered',
|
|
541
|
+
data: { category: 'global-control', color: 'namespace' }
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
label: 'enable-lint',
|
|
545
|
+
kind: node_1.CompletionItemKind.Module, // Module type for global controls
|
|
546
|
+
detail: 'Enable all linting (default state)',
|
|
547
|
+
insertText: 'enable-lint',
|
|
548
|
+
documentation: 'Enables linting and validation (this is the default state)',
|
|
549
|
+
data: { category: 'global-control', color: 'namespace' }
|
|
550
|
+
},
|
|
551
|
+
// Line-specific control (EnumMember category)
|
|
552
|
+
{
|
|
553
|
+
label: 'disable-next-line',
|
|
554
|
+
kind: node_1.CompletionItemKind.EnumMember, // EnumMember for line-specific
|
|
555
|
+
detail: 'Disable lint for the next line only',
|
|
556
|
+
insertText: 'disable-next-line',
|
|
557
|
+
documentation: 'Disables linting and validation for the next line only',
|
|
558
|
+
data: { category: 'line-control', color: 'enumMember' }
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
label: 'disable-line',
|
|
562
|
+
kind: node_1.CompletionItemKind.EnumMember, // EnumMember for line-specific
|
|
563
|
+
detail: 'Disable lint for current line',
|
|
564
|
+
insertText: 'disable-line',
|
|
565
|
+
documentation: 'Disables linting and validation for the current line',
|
|
566
|
+
data: { category: 'line-control', color: 'enumMember' }
|
|
567
|
+
},
|
|
568
|
+
// Rule-specific control (Type category)
|
|
569
|
+
{
|
|
570
|
+
label: 'disable-rule',
|
|
571
|
+
kind: node_1.CompletionItemKind.TypeParameter, // TypeParameter for rule-specific
|
|
572
|
+
detail: 'Disable specific rule(s)',
|
|
573
|
+
insertText: 'disable-rule ${1:rule-name}',
|
|
574
|
+
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
575
|
+
documentation: 'Disables specific validation rules. Example: @disable-rule missing-id, invalid-operator',
|
|
576
|
+
data: { category: 'rule-control', color: 'type' }
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
label: 'enable-rule',
|
|
580
|
+
kind: node_1.CompletionItemKind.TypeParameter, // TypeParameter for rule-specific
|
|
581
|
+
detail: 'Enable specific rule(s)',
|
|
582
|
+
insertText: 'enable-rule ${1:rule-name}',
|
|
583
|
+
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
584
|
+
documentation: 'Enables specific validation rules that were previously disabled',
|
|
585
|
+
data: { category: 'rule-control', color: 'type' }
|
|
586
|
+
}
|
|
587
|
+
];
|
|
588
|
+
return directives;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Get file path completions for import directives
|
|
592
|
+
*/
|
|
593
|
+
function getImportFileCompletions(documentPath, partialPath) {
|
|
594
|
+
const completions = [];
|
|
595
|
+
try {
|
|
596
|
+
const decodedUri = decodeURIComponent(documentPath);
|
|
597
|
+
const documentDir = (0, path_1.dirname)(decodedUri.replace('file:///', '').replace(/^\/([A-Z]:)/, '$1'));
|
|
598
|
+
const currentDir = partialPath.includes('/') ? (0, path_1.dirname)((0, path_1.join)(documentDir, partialPath)) : documentDir;
|
|
599
|
+
// Get list of JSON and YAML files in the directory
|
|
600
|
+
const fs = require('fs');
|
|
601
|
+
if ((0, fs_1.existsSync)(currentDir)) {
|
|
602
|
+
const files = fs.readdirSync(currentDir);
|
|
603
|
+
const validExtensions = ['.json', '.yaml', '.yml'];
|
|
604
|
+
files.forEach((file) => {
|
|
605
|
+
const ext = (0, path_1.extname)(file).toLowerCase();
|
|
606
|
+
if (validExtensions.includes(ext)) {
|
|
607
|
+
const relativePath = './' + (partialPath.includes('/') ?
|
|
608
|
+
(0, path_1.dirname)(partialPath) + '/' + file : file);
|
|
609
|
+
completions.push({
|
|
610
|
+
label: relativePath,
|
|
611
|
+
kind: node_1.CompletionItemKind.File,
|
|
612
|
+
detail: `${ext.toUpperCase()} data file`,
|
|
613
|
+
insertText: `"${relativePath}"`
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
console.log(`[LSP] Error getting file completions:`, error);
|
|
621
|
+
}
|
|
622
|
+
return completions;
|
|
623
|
+
}
|
|
624
|
+
//# sourceMappingURL=completions.js.map
|