@oml/language 0.7.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/README.md +44 -0
- package/out/oml/generated/ast.d.ts +2109 -0
- package/out/oml/generated/ast.js +1807 -0
- package/out/oml/generated/ast.js.map +1 -0
- package/out/oml/generated/grammar.d.ts +6 -0
- package/out/oml/generated/grammar.js +6885 -0
- package/out/oml/generated/grammar.js.map +1 -0
- package/out/oml/generated/module.d.ts +13 -0
- package/out/oml/generated/module.js +21 -0
- package/out/oml/generated/module.js.map +1 -0
- package/out/oml/index.d.ts +17 -0
- package/out/oml/index.js +19 -0
- package/out/oml/index.js.map +1 -0
- package/out/oml/oml-candidates.d.ts +27 -0
- package/out/oml/oml-candidates.js +146 -0
- package/out/oml/oml-candidates.js.map +1 -0
- package/out/oml/oml-code-actions.d.ts +6 -0
- package/out/oml/oml-code-actions.js +79 -0
- package/out/oml/oml-code-actions.js.map +1 -0
- package/out/oml/oml-completion.d.ts +50 -0
- package/out/oml/oml-completion.js +188 -0
- package/out/oml/oml-completion.js.map +1 -0
- package/out/oml/oml-converter.d.ts +10 -0
- package/out/oml/oml-converter.js +62 -0
- package/out/oml/oml-converter.js.map +1 -0
- package/out/oml/oml-document-validator.d.ts +10 -0
- package/out/oml/oml-document-validator.js +31 -0
- package/out/oml/oml-document-validator.js.map +1 -0
- package/out/oml/oml-document.d.ts +9 -0
- package/out/oml/oml-document.js +18 -0
- package/out/oml/oml-document.js.map +1 -0
- package/out/oml/oml-edit.d.ts +72 -0
- package/out/oml/oml-edit.js +1155 -0
- package/out/oml/oml-edit.js.map +1 -0
- package/out/oml/oml-formatter.d.ts +22 -0
- package/out/oml/oml-formatter.js +357 -0
- package/out/oml/oml-formatter.js.map +1 -0
- package/out/oml/oml-hover.d.ts +13 -0
- package/out/oml/oml-hover.js +71 -0
- package/out/oml/oml-hover.js.map +1 -0
- package/out/oml/oml-index-manager.d.ts +10 -0
- package/out/oml/oml-index-manager.js +48 -0
- package/out/oml/oml-index-manager.js.map +1 -0
- package/out/oml/oml-index.d.ts +20 -0
- package/out/oml/oml-index.js +133 -0
- package/out/oml/oml-index.js.map +1 -0
- package/out/oml/oml-module.d.ts +42 -0
- package/out/oml/oml-module.js +76 -0
- package/out/oml/oml-module.js.map +1 -0
- package/out/oml/oml-rename.d.ts +14 -0
- package/out/oml/oml-rename.js +114 -0
- package/out/oml/oml-rename.js.map +1 -0
- package/out/oml/oml-scope.d.ts +30 -0
- package/out/oml/oml-scope.js +225 -0
- package/out/oml/oml-scope.js.map +1 -0
- package/out/oml/oml-serializer.d.ts +2 -0
- package/out/oml/oml-serializer.js +883 -0
- package/out/oml/oml-serializer.js.map +1 -0
- package/out/oml/oml-utils.d.ts +53 -0
- package/out/oml/oml-utils.js +241 -0
- package/out/oml/oml-utils.js.map +1 -0
- package/out/oml/oml-validator.d.ts +49 -0
- package/out/oml/oml-validator.js +668 -0
- package/out/oml/oml-validator.js.map +1 -0
- package/out/oml/oml-workspace.d.ts +23 -0
- package/out/oml/oml-workspace.js +68 -0
- package/out/oml/oml-workspace.js.map +1 -0
- package/package.json +50 -0
- package/src/oml/generated/ast.ts +2641 -0
- package/src/oml/generated/grammar.ts +6887 -0
- package/src/oml/generated/module.ts +25 -0
- package/src/oml/index.ts +19 -0
- package/src/oml/oml-candidates.ts +176 -0
- package/src/oml/oml-code-actions.ts +120 -0
- package/src/oml/oml-completion.ts +222 -0
- package/src/oml/oml-converter.ts +66 -0
- package/src/oml/oml-document-validator.ts +39 -0
- package/src/oml/oml-document.ts +24 -0
- package/src/oml/oml-edit.ts +1292 -0
- package/src/oml/oml-formatter.ts +390 -0
- package/src/oml/oml-hover.ts +93 -0
- package/src/oml/oml-index-manager.ts +56 -0
- package/src/oml/oml-index.ts +145 -0
- package/src/oml/oml-module.ts +105 -0
- package/src/oml/oml-rename.ts +140 -0
- package/src/oml/oml-scope.ts +279 -0
- package/src/oml/oml-serializer.ts +1080 -0
- package/src/oml/oml-utils.ts +294 -0
- package/src/oml/oml-validator.ts +725 -0
- package/src/oml/oml-workspace.ts +81 -0
- package/src/oml/oml.langium +594 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
isDescription,
|
|
5
|
+
isForwardRelation,
|
|
6
|
+
isMember,
|
|
7
|
+
isRelationEntity,
|
|
8
|
+
isReverseRelation,
|
|
9
|
+
isOntology,
|
|
10
|
+
isVocabulary,
|
|
11
|
+
} from './generated/ast.js';
|
|
12
|
+
import type { LangiumDocument, LangiumDocuments } from 'langium';
|
|
13
|
+
import type {
|
|
14
|
+
Description,
|
|
15
|
+
ForwardRelation,
|
|
16
|
+
Import,
|
|
17
|
+
Member,
|
|
18
|
+
Ontology,
|
|
19
|
+
ReverseRelation,
|
|
20
|
+
Vocabulary,
|
|
21
|
+
} from './generated/ast.js';
|
|
22
|
+
import { URI } from 'langium';
|
|
23
|
+
|
|
24
|
+
export type NamedElement = Member | ForwardRelation | ReverseRelation;
|
|
25
|
+
export type IriOwningElement = NamedElement | Ontology;
|
|
26
|
+
|
|
27
|
+
type NamedCandidate = { name?: unknown };
|
|
28
|
+
|
|
29
|
+
function resolveName(candidate: NamedCandidate): string | undefined {
|
|
30
|
+
const value = candidate?.name;
|
|
31
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getMemberName(member: Member): string | undefined {
|
|
38
|
+
return resolveName(member);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getNamedElementName(element: NamedElement): string | undefined {
|
|
42
|
+
return resolveName(element);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function findOwningOntology(member: Member): Vocabulary | Description | undefined {
|
|
46
|
+
let current: any = (member as any).$container;
|
|
47
|
+
while (current) {
|
|
48
|
+
if (isVocabulary(current) || isDescription(current)) {
|
|
49
|
+
return current;
|
|
50
|
+
}
|
|
51
|
+
current = current.$container;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function findOwningOntologyNode(node: any): Ontology | undefined {
|
|
57
|
+
let current = node;
|
|
58
|
+
while (current) {
|
|
59
|
+
if (isOntology(current)) {
|
|
60
|
+
return current as Ontology;
|
|
61
|
+
}
|
|
62
|
+
current = current.$container;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function collectOntologyMembers(ontology: Vocabulary | Description): NamedElement[] {
|
|
68
|
+
const members: NamedElement[] = [];
|
|
69
|
+
const statements = (ontology as Vocabulary | Description).ownedStatements ?? [];
|
|
70
|
+
for (const statement of statements) {
|
|
71
|
+
if (isMember(statement)) {
|
|
72
|
+
members.push(statement);
|
|
73
|
+
}
|
|
74
|
+
if (isRelationEntity(statement)) {
|
|
75
|
+
if (statement.forwardRelation) {
|
|
76
|
+
members.push(statement.forwardRelation);
|
|
77
|
+
}
|
|
78
|
+
if (statement.reverseRelation) {
|
|
79
|
+
members.push(statement.reverseRelation);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return members;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const humanizeTypeName = (typeName: string): string =>
|
|
87
|
+
typeName
|
|
88
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
89
|
+
.replace(/_/g, ' ')
|
|
90
|
+
.toLowerCase();
|
|
91
|
+
|
|
92
|
+
export interface SourceLocation {
|
|
93
|
+
startLine: number;
|
|
94
|
+
startColumn: number;
|
|
95
|
+
endLine: number;
|
|
96
|
+
endColumn: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getSourceLocation(astNode: { $cstNode?: { offset: number; length: number }; $document?: { textDocument?: { positionAt(offset: number): { line: number; character: number } } } } | undefined): SourceLocation | undefined {
|
|
100
|
+
const cstNode = astNode?.$cstNode;
|
|
101
|
+
if (!cstNode) return undefined;
|
|
102
|
+
|
|
103
|
+
const doc = astNode.$document;
|
|
104
|
+
if (!doc?.textDocument) return undefined;
|
|
105
|
+
|
|
106
|
+
const startPosition = doc.textDocument.positionAt(cstNode.offset);
|
|
107
|
+
const endPosition = doc.textDocument.positionAt(cstNode.offset + cstNode.length);
|
|
108
|
+
return {
|
|
109
|
+
startLine: startPosition.line + 1,
|
|
110
|
+
startColumn: startPosition.character,
|
|
111
|
+
endLine: endPosition.line + 1,
|
|
112
|
+
endColumn: endPosition.character,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function deref(node: any): any {
|
|
117
|
+
if (!node) return undefined;
|
|
118
|
+
const wrapper = node.ref;
|
|
119
|
+
if (wrapper) return wrapper.ref ?? wrapper._ref ?? wrapper;
|
|
120
|
+
return node;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const normalizeNamespace = (raw: string): string =>
|
|
124
|
+
raw.replace(/^<|>$/g, '').replace(/[#/]?$/, '');
|
|
125
|
+
|
|
126
|
+
export const resolveImportNamespace = (imp: Import): string => {
|
|
127
|
+
const raw =
|
|
128
|
+
(imp.imported as any)?.$refText ??
|
|
129
|
+
(imp as any)?.imported ??
|
|
130
|
+
'';
|
|
131
|
+
return raw.replace(/^<|>$/g, '');
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const getAllLangiumDocuments = (documents: LangiumDocuments): LangiumDocument[] => {
|
|
135
|
+
const docs: any = documents;
|
|
136
|
+
const all = docs.all ?? [];
|
|
137
|
+
if (Array.isArray(all)) {
|
|
138
|
+
return all;
|
|
139
|
+
}
|
|
140
|
+
if (typeof all?.toArray === 'function') {
|
|
141
|
+
return all.toArray() as LangiumDocument[];
|
|
142
|
+
}
|
|
143
|
+
return Array.from(all as Iterable<LangiumDocument>);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const getLangiumDocumentUri = (document: LangiumDocument): string => {
|
|
147
|
+
const anyDoc: any = document;
|
|
148
|
+
const uri = anyDoc.uri ?? anyDoc.textDocument?.uri;
|
|
149
|
+
return typeof uri?.toString === 'function' ? uri.toString() : String(uri ?? '');
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const getLangiumDocumentVersion = (document: LangiumDocument): number => {
|
|
153
|
+
const version = (document as any)?.textDocument?.version;
|
|
154
|
+
return typeof version === 'number' ? version : 0;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const getWorkspaceSnapshot = (documents: LangiumDocuments): string =>
|
|
158
|
+
getAllLangiumDocuments(documents)
|
|
159
|
+
.map((doc) => `${getLangiumDocumentUri(doc)}@${getLangiumDocumentVersion(doc)}`)
|
|
160
|
+
.sort()
|
|
161
|
+
.join('|');
|
|
162
|
+
|
|
163
|
+
const GITHUB_COPILOT_TRANSIENT_DOCUMENT_SCHEMES = new Set([
|
|
164
|
+
'chat-editing-text-model',
|
|
165
|
+
'chat-editing-snapshot-text-model',
|
|
166
|
+
'vscode-chat-code-block',
|
|
167
|
+
]);
|
|
168
|
+
|
|
169
|
+
export function isTransientEditorDocumentUri(uri: string): boolean {
|
|
170
|
+
try {
|
|
171
|
+
return GITHUB_COPILOT_TRANSIENT_DOCUMENT_SCHEMES.has(URI.parse(uri).scheme);
|
|
172
|
+
} catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export const splitIri = (iri: string): { base: string; fragment: string; separator: string } | null => {
|
|
178
|
+
const trimmed = iri.trim();
|
|
179
|
+
if (!trimmed) return null;
|
|
180
|
+
const hashIndex = trimmed.lastIndexOf('#');
|
|
181
|
+
if (hashIndex >= 0) {
|
|
182
|
+
return {
|
|
183
|
+
base: trimmed.slice(0, hashIndex),
|
|
184
|
+
fragment: trimmed.slice(hashIndex + 1),
|
|
185
|
+
separator: '#',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const slashIndex = trimmed.lastIndexOf('/');
|
|
189
|
+
if (slashIndex >= 0) {
|
|
190
|
+
return {
|
|
191
|
+
base: trimmed.slice(0, slashIndex),
|
|
192
|
+
fragment: trimmed.slice(slashIndex + 1),
|
|
193
|
+
separator: '/',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const resolveIndent = (text: string, rootOffset: number, insertOffset: number): string => {
|
|
200
|
+
const slice = text.slice(rootOffset, insertOffset);
|
|
201
|
+
const lines = slice.split(/\r?\n/);
|
|
202
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
203
|
+
const line = lines[i];
|
|
204
|
+
if (!line || !line.trim() || line.trim() === '}') continue;
|
|
205
|
+
const match = line.match(/^\s*/);
|
|
206
|
+
if (match) return match[0];
|
|
207
|
+
}
|
|
208
|
+
return ' ';
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const getOntologyMemberNames = (root: Ontology): string[] => {
|
|
212
|
+
const statements = (root as { ownedStatements?: any[] }).ownedStatements ?? [];
|
|
213
|
+
return statements
|
|
214
|
+
.filter((stmt: any) => isMember(stmt) && typeof stmt?.name === 'string')
|
|
215
|
+
.map((stmt: any) => stmt.name.trim())
|
|
216
|
+
.filter(Boolean);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export function findOntologyMemberByName(root: Ontology, memberName: string): NamedElement | undefined {
|
|
220
|
+
const wanted = memberName.trim();
|
|
221
|
+
if (!wanted) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
if (!isVocabulary(root) && !isDescription(root)) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
return collectOntologyMembers(root).find((member) => getNamedElementName(member) === wanted);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function getIriOwningElement(node: any): IriOwningElement | undefined {
|
|
231
|
+
let current = node;
|
|
232
|
+
while (current) {
|
|
233
|
+
if (isMember(current) || isForwardRelation(current) || isReverseRelation(current) || isOntology(current)) {
|
|
234
|
+
return current as IriOwningElement;
|
|
235
|
+
}
|
|
236
|
+
current = current.$container;
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function getNamespaceSeparator(rawNamespace: string | undefined): '#' | '/' {
|
|
242
|
+
const value = (rawNamespace ?? '').trim();
|
|
243
|
+
if (value.endsWith('/')) return '/';
|
|
244
|
+
if (value.endsWith('#')) return '#';
|
|
245
|
+
return '#';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function buildMemberIri(namespace: string, memberName: string, separator: '#' | '/'): string {
|
|
249
|
+
const ns = normalizeNamespace(namespace);
|
|
250
|
+
return `${ns}${separator}${memberName}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function getIriForNode(node: any): string | undefined {
|
|
254
|
+
const owner = getIriOwningElement(node);
|
|
255
|
+
if (!owner) return undefined;
|
|
256
|
+
|
|
257
|
+
if (isOntology(owner)) {
|
|
258
|
+
const namespace = typeof owner.namespace === 'string' ? owner.namespace : '';
|
|
259
|
+
const normalized = normalizeNamespace(namespace);
|
|
260
|
+
return normalized || undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const name = getNamedElementName(owner);
|
|
264
|
+
if (!name) return undefined;
|
|
265
|
+
const ontology = findOwningOntologyNode(owner);
|
|
266
|
+
if (!ontology || typeof ontology.namespace !== 'string') return undefined;
|
|
267
|
+
const separator = getNamespaceSeparator(ontology.namespace);
|
|
268
|
+
return buildMemberIri(ontology.namespace, name, separator);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function getDocumentUriForNode(node: any): string | undefined {
|
|
272
|
+
let current = node;
|
|
273
|
+
while (current) {
|
|
274
|
+
const uri = current?.$document?.uri?.toString?.();
|
|
275
|
+
if (uri) {
|
|
276
|
+
return uri;
|
|
277
|
+
}
|
|
278
|
+
current = current.$container;
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function getModelIdForNode(
|
|
284
|
+
node: any,
|
|
285
|
+
astNodeLocator: { getAstNodePath(node: any): string } | undefined,
|
|
286
|
+
defaultDocumentUri?: string
|
|
287
|
+
): string | undefined {
|
|
288
|
+
const docUri = getDocumentUriForNode(node) ?? defaultDocumentUri;
|
|
289
|
+
if (!docUri) return undefined;
|
|
290
|
+
if (!astNodeLocator || typeof astNodeLocator.getAstNodePath !== 'function') return undefined;
|
|
291
|
+
const astPath = astNodeLocator.getAstNodePath(node);
|
|
292
|
+
if (!astPath) return undefined;
|
|
293
|
+
return `${docUri}#${astPath}`;
|
|
294
|
+
}
|