Skip to content

Commit 52d3211

Browse files
committed
#30 Add rootId API to SemanticDomain
1 parent 7944b71 commit 52d3211

File tree

10 files changed

+62
-45
lines changed

10 files changed

+62
-45
lines changed

src/langium-model-server/lms/facade.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ implements LangiumModelServerFacade<SM> {
7070
if (!this.langiumDocuments.hasDocument(documentUri)) {
7171
return undefined
7272
}
73-
const langiumDocument = this.langiumDocuments.getOrCreateDocument(documentUri)
74-
return this.identityManager.getIdentityIndex(langiumDocument).id
73+
const document = this.langiumDocuments.getOrCreateDocument(documentUri)
74+
if (this.isLmsDocument(document)) {
75+
return this.identityManager.getIdentityIndex(document).id
76+
}
77+
return undefined
7578
}
7679

7780
public getById(id: string): SM | undefined {
@@ -116,7 +119,7 @@ implements LangiumModelServerFacade<SM> {
116119
return undefined
117120
}
118121
// NOTE: Since document URI is known to SemanticIndexManager, this LangiumDocument is LmsDocument
119-
const document: LmsDocument = this.langiumDocuments.getOrCreateDocument(documentUri)
122+
const document = this.langiumDocuments.getOrCreateDocument(documentUri)
120123
if (!this.isLmsDocument(document)) {
121124
throw new Error('Supplied ID is not compatible with LMSDocument type served')
122125
}

src/langium-model-server/lsp/document-highlight-provider.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22
import type { AstNode, LangiumDocument, MaybePromise } from 'langium'
33
import { DefaultDocumentHighlightProvider, findLeafNodeAtOffset, getContainerOfType } from 'langium'
44
import type { DocumentHighlight, DocumentHighlightParams } from 'vscode-languageserver'
5-
import type * as identity from '../identity/model'
65
import type { IdentityIndex } from '../identity'
76
import type { IdentityManager } from '../identity/manager'
8-
import * as semantic from '../semantic/model'
9-
import type { LangiumModelServerServices } from '../services'
7+
import type * as identity from '../identity/model'
108
import * as source from '../lms/model'
119
import type { LmsSubscriptions } from '../lms/subscriptions'
12-
import type { LmsDocument, SemanticAwareDocument } from '../workspace/documents'
10+
import * as semantic from '../semantic/model'
11+
import type { LangiumModelServerServices } from '../services'
12+
import type { TypeGuard } from '../utils/types'
13+
import type { ExtendableLangiumDocument } from '../workspace/documents'
14+
import type { LmsDocument } from '../workspace/documents'
1315

1416
export class LmsDocumentHighlightProvider<SM extends identity.SemanticIdentity, II extends IdentityIndex<SM>, D extends LmsDocument> extends DefaultDocumentHighlightProvider {
1517

1618
private lmsSubscriptions: LmsSubscriptions<SM>
1719
private identityManager: IdentityManager
20+
private isLmsDocument: TypeGuard<LmsDocument, ExtendableLangiumDocument>
1821

1922
private highlightedNodeIdByModelId: Map<string, string> = new Map()
2023
private highlightPushingTimeout: NodeJS.Timeout
@@ -23,9 +26,10 @@ export class LmsDocumentHighlightProvider<SM extends identity.SemanticIdentity,
2326
super(services)
2427
this.lmsSubscriptions = services.lms.LmsSubscriptions
2528
this.identityManager = services.identity.IdentityManager
29+
this.isLmsDocument = services.workspace.LmsDocumentGuard
2630
}
2731

28-
override getDocumentHighlight(document: LangiumDocument & SemanticAwareDocument, params: DocumentHighlightParams): MaybePromise<DocumentHighlight[] | undefined> {
32+
override getDocumentHighlight(document: LangiumDocument, params: DocumentHighlightParams): MaybePromise<DocumentHighlight[] | undefined> {
2933
const rootNode = document.parseResult.value.$cstNode
3034
if (!rootNode) {
3135
return undefined
@@ -35,7 +39,7 @@ export class LmsDocumentHighlightProvider<SM extends identity.SemanticIdentity,
3539
return undefined
3640
}
3741

38-
if (document.semanticDomain) {
42+
if (this.isLmsDocument(document)) {
3943
if (this.highlightPushingTimeout) {
4044
clearInterval(this.highlightPushingTimeout)
4145
}
@@ -45,7 +49,7 @@ export class LmsDocumentHighlightProvider<SM extends identity.SemanticIdentity,
4549
return super.getDocumentHighlight(document, params)
4650
}
4751

48-
private calculateAndPushHighlight(document: LangiumDocument, selectedAstNode: AstNode) {
52+
private calculateAndPushHighlight(document: LmsDocument, selectedAstNode: AstNode) {
4953
const highlightedNodeId = getContainerOfType(selectedAstNode, semantic.Identified.is)?.id
5054
const modelId = this.identityManager.getIdentityIndex(document).id
5155
if (highlightedNodeId && highlightedNodeId !== this.highlightedNodeIdByModelId.get(modelId)) {

src/langium-model-server/semantic/model.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ export type ArtificialIndexedAstNode = ArtificialAstNode & {
4444
readonly $containerIndex: number
4545
}
4646

47-
export type AstRootNode = Valid<AstNode> & {
48-
readonly $document: LangiumDocument
47+
export type AstRootNode<T extends AstNode = AstNode> = T & {
48+
readonly $document: LangiumDocument<T>
4949
}
5050

5151
export namespace AstRootNode {

src/langium-model-server/semantic/semantic-domain.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import type { AstRootNode, IdentifiedNode, IdentifiedRoot } from './model'
22

33
export interface SemanticDomain {
44
clear(): void
5+
readonly rootId: string
56
/**
67
* Maps the `rootNode` with semantic ID
78
* @param rootNode Am AST Root node of the document
89
* @param semanticId Semantic ID, which {@link rootNode} is identified with
910
*/
10-
identifyRootNode(rootNode: AstRootNode, semanticId: string): IdentifiedRoot
11-
readonly identifiedRootNode: IdentifiedRoot | undefined
11+
identifyRoot(rootNode: AstRootNode): IdentifiedRoot
12+
readonly identifiedRoot: IdentifiedRoot | undefined
1213
getIdentifiedNode(id: string): IdentifiedNode | undefined
1314
}
1415

15-
export type SemanticDomainFactory = () => SemanticDomain
16+
export type SemanticDomainFactory = (semanticId: string) => SemanticDomain

src/langium-model-server/workspace/documents.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { AstNode, LangiumDocument } from 'langium'
22
import { DocumentState } from 'langium'
33
import type { SemanticDomain } from '../semantic/semantic-domain'
44
import type { Override } from '../utils/types'
5+
import type * as sem from '../semantic/model'
56

67
export type ExtendableLangiumDocument<T extends AstNode = AstNode> = Override<LangiumDocument<T>, 'state', number>
78

@@ -11,8 +12,11 @@ export type ExtendableLangiumDocument<T extends AstNode = AstNode> = Override<La
1112
*/
1213
export type LmsDocument<T extends AstNode = AstNode> = ExtendableLangiumDocument<T> & SemanticAwareDocument & {
1314
state: LmsDocumentState
15+
parseResult: {
16+
// TODO: Suggest AstRootNode as a specific interface in Langium library
17+
value: sem.AstRootNode<T>
18+
}
1419
}
15-
1620
export namespace LmsDocument {
1721
export function isInitialized<T extends AstNode>(lmsDocument: LmsDocument<T>): lmsDocument is Initialized<LmsDocument<T>> {
1822
return lmsDocument.semanticDomain !== undefined

src/langium-model-server/workspace/lms-document-builder.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export class DefaultLmsDocumentBuilder<SM extends id.SemanticIdentity, II extend
4646
await interruptAndCheck(cancelToken)
4747
if (!lmsDocument.semanticDomain) {
4848
console.log(`Initializing semantic domain for ${document.uri.toString()}`)
49-
lmsDocument.semanticDomain = this.createSemanticDomain()
49+
const identityIndex = this.identityManager.getIdentityIndex(lmsDocument)
50+
lmsDocument.semanticDomain = this.createSemanticDomain(identityIndex.id)
5051
} else {
5152
lmsDocument.semanticDomain.clear()
5253
}
@@ -58,10 +59,10 @@ export class DefaultLmsDocumentBuilder<SM extends id.SemanticIdentity, II extend
5859
console.debug('====== IDENTITY RECONCILIATION PHASE ======')
5960
const newUpdatesForLmsDocuments: Map<Initialized<D>, src.RootUpdate<SM>> = new Map()
6061
for (const document of documents) {
61-
const semanticId = this.identityManager.getIdentityIndex(document).id
6262
const lmsDocument: ExtendableLangiumDocument = document
6363
// NOTE: Actually, all LMS Documents are initialized during `initializeSemanticDomain` phase
6464
if (this.isLmsDocument(lmsDocument) && LmsDocument.isInitialized(lmsDocument)) {
65+
const semanticId = lmsDocument.semanticDomain.rootId
6566
newUpdatesForLmsDocuments.set(lmsDocument, src.RootUpdate.createEmpty<SM>(semanticId, id.ModelUri.root))
6667
}
6768
}

src/language-server/task-list/lms/model/model.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type * as id from '../../../../langium-model-server/semantic/model'
21
import type { Task } from './task'
32
import type { Transition } from './transition'
43

@@ -9,9 +8,9 @@ export interface Model {
98
}
109

1110
export namespace Model {
12-
export function create(rootModel: id.IdentifiedRoot): Model {
11+
export function createEmpty(rootId: string): Model {
1312
return {
14-
id: rootModel.id,
13+
id: rootId,
1514
tasks: [],
1615
transitions: []
1716
}

src/language-server/task-list/lms/task-list-facade.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { EditingResult } from '../../../langium-model-server/lms/model'
99
import type { LmsSubscriptions } from '../../../langium-model-server/lms/subscriptions'
1010
import * as id from '../../../langium-model-server/semantic/model'
1111
import type { LangiumModelServerServices } from '../../../langium-model-server/services'
12-
import type { LmsDocument } from '../../../langium-model-server/workspace/documents'
12+
import { LmsDocument } from '../../../langium-model-server/workspace/documents'
1313
import * as ast from '../../generated/ast'
1414
import type { TaskListIdentityIndex } from '../identity'
1515
import * as identity from '../identity/model'
@@ -394,10 +394,10 @@ export class TaskListLangiumModelServerFacade extends AbstractLangiumModelServer
394394

395395
protected override convertSemanticModelToSourceModel(lmsDocument: LmsDocument): Model | undefined {
396396

397-
if (!isTaskListDocument(lmsDocument) || !lmsDocument.semanticDomain?.identifiedRootNode) {
397+
if (!isTaskListDocument(lmsDocument) || !LmsDocument.isInitialized(lmsDocument)) {
398398
return undefined
399399
}
400-
const sourceModel = Model.create(lmsDocument.semanticDomain.identifiedRootNode)
400+
const sourceModel = Model.createEmpty(lmsDocument.semanticDomain.rootId)
401401
for (const task of lmsDocument.semanticDomain.identifiedTasks.values()) {
402402
sourceModel.tasks.push(Task.create(task))
403403
}

src/language-server/task-list/semantic/task-list-identity-reconciler.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import * as src from '../../../langium-model-server/lms/model'
22
import type { IdentityReconciler } from '../../../langium-model-server/semantic/identity-reconciler'
3-
import { AstRootNode } from '../../../langium-model-server/semantic/model'
4-
import type { Initialized } from '../../../langium-model-server/workspace/documents'
3+
import type { Initialized, LmsDocument } from '../../../langium-model-server/workspace/documents'
54
import type * as ast from '../../generated/ast'
5+
import type { TaskListIdentityManager } from '../identity/manager'
66
import type * as source from '../lms/model'
77
import type { TaskListModelUpdateCalculators } from '../lms/task-list-model-update-calculation'
88
import type { TaskListServices } from '../task-list-module'
99
import { type TaskListDocument } from '../workspace/documents'
10-
import type { TaskListIdentityManager } from '../identity/manager'
1110

1211
export class TaskListIdentityReconciler implements IdentityReconciler<source.Model, TaskListDocument>{
1312
private identityManager: TaskListIdentityManager
@@ -29,26 +28,29 @@ export class TaskListIdentityReconciler implements IdentityReconciler<source.Mod
2928
= which AST node I assume correct enough to track his identity?
3029
*/
3130
identityReconciliationIterations = [
31+
// NOTE: ITERATION 0: mapping Root node
32+
this.identifyRoot.bind(this),
3233
// NOTE: ITERATION 1: mapping Tasks
3334
this.reconcileTasks.bind(this),
3435
// NOTE: ITERATION 2: mapping Transitions
3536
this.reconcileTransitions.bind(this),
3637
]
3738

39+
// TODO: Extract it to an abstract superclass
40+
private identifyRoot(document: Initialized<LmsDocument>) {
41+
document.semanticDomain.identifyRoot(document.parseResult.value)
42+
}
43+
3844
// Example of how Identity of Ast-based element is reconciled
3945
private reconcileTasks(document: Initialized<TaskListDocument>, update: src.Update<source.Model>) {
4046

4147
// NOTE: Here I am expressing an idea, that perhaps I will have to have some sort of nested model indices,
4248
// which would make it generally necessary to pass the parent model into the semantic domain when requesting some (valid/identified) models
4349
const astModel: ast.Model = document.parseResult.value
50+
4451
const identityIndex = this.identityManager.getIdentityIndex(document)
45-
// TODO: Suggest AstRootNode as a specific interface in Langium library
46-
if (!AstRootNode.is(astModel)) {
47-
throw new Error('Expected Model to be a root node, but somehow it was not!. Model: ' + astModel)
48-
}
4952
const semanticDomain = document.semanticDomain
50-
semanticDomain.identifyRootNode(astModel, identityIndex.id)
51-
const updateCalculator = this.modelUpdateCalculators.getOrCreateCalculator(document, identityIndex.id)
53+
const updateCalculator = this.modelUpdateCalculators.getOrCreateCalculator(document, semanticDomain.rootId)
5254

5355
const existingUnmappedTasks = identityIndex.tasksByName
5456
// Actual mapping: marking semantic elements for deletion, and AST nodes to be added
@@ -74,8 +76,8 @@ export class TaskListIdentityReconciler implements IdentityReconciler<source.Mod
7476
private reconcileTransitions(document: Initialized<TaskListDocument>, update: src.Update<source.Model>) {
7577

7678
const identityIndex = this.identityManager.getIdentityIndex(document)
77-
const updateCalculator = this.modelUpdateCalculators.getOrCreateCalculator(document, identityIndex.id)
7879
const semanticDomain = document.semanticDomain
80+
const updateCalculator = this.modelUpdateCalculators.getOrCreateCalculator(document, semanticDomain.rootId)
7981

8082
const existingUnmappedTransitions = identityIndex.transitionsByName
8183
semanticDomain.getValidTransitions()

src/language-server/task-list/semantic/task-list-semantic-domain.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import type { ArtificialAstNode, AstRootNode, IdentifiedRoot, Valid } from '../.
44
import { Identified } from '../../../langium-model-server/semantic/model'
55
import type { SemanticDomain } from '../../../langium-model-server/semantic/semantic-domain'
66
import type * as ast from '../../generated/ast'
7-
import { Transition } from './model'
87
import type * as id from '../identity/model'
8+
import { Transition } from './model'
99

1010
export interface QueriableTaskListSemanticDomain {
1111
readonly identifiedTasks: ReadonlyMap<string, Identified<ast.Task>>
@@ -39,24 +39,27 @@ export interface TaskListSemanticDomain extends QueriableTaskListSemanticDomain,
3939

4040
export namespace TaskListSemanticDomain {
4141

42-
export function create() {
43-
return new DefaultTaskListSemanticDomain()
42+
export function create(rootId: string) {
43+
return new DefaultTaskListSemanticDomain(rootId)
4444
}
4545
}
4646

4747
class DefaultTaskListSemanticDomain implements TaskListSemanticDomain {
4848

49+
public readonly rootId: string
50+
4951
protected invalidTasks: Set<ast.Task>
5052
protected invalidReferences: Map<ast.Task, Set<number>>
5153

52-
private _identifiedRootNode: IdentifiedRoot | undefined
54+
private _identifiedRoot: IdentifiedRoot | undefined
5355
private _identifiedTasksById: Map<string, Identified<ast.Task>>
5456
private _previousIdentifiedTaskById: Map<string, Identified<ast.Task>> | undefined
5557
private _validTransitionsByIdentifiedTask: Map<Identified<ast.Task>, Transition[]>
5658
private _identifiedTransitionsById: Map<string, Identified<Transition>>
5759
private _previousIdentifiedTransitionsById: Map<string, Identified<Transition>> | undefined
5860

59-
constructor() {
61+
constructor(rootId: string) {
62+
this.rootId = rootId
6063
this.invalidTasks = new Set()
6164
this.invalidReferences = new Map()
6265
this._identifiedTasksById = new Map()
@@ -103,9 +106,9 @@ class DefaultTaskListSemanticDomain implements TaskListSemanticDomain {
103106
.flatMap(this.getValidTransitionsForSourceTask.bind(this))
104107
}
105108

106-
public identifyRootNode(rootNode: AstRootNode, semanticId: string): IdentifiedRoot {
107-
this._identifiedRootNode = Identified.identifyRoot(rootNode, semanticId)
108-
return this._identifiedRootNode
109+
public identifyRoot(rootNode: AstRootNode): IdentifiedRoot {
110+
this._identifiedRoot = Identified.identifyRoot(rootNode, this.rootId)
111+
return this._identifiedRoot
109112
}
110113

111114
public identifyTask(task: Valid<ast.Task>, identity: id.TaskIdentity): Identified<ast.Task> {
@@ -120,8 +123,8 @@ class DefaultTaskListSemanticDomain implements TaskListSemanticDomain {
120123
return identifiedTransition
121124
}
122125

123-
get identifiedRootNode(): IdentifiedRoot | undefined {
124-
return this._identifiedRootNode
126+
get identifiedRoot(): IdentifiedRoot | undefined {
127+
return this._identifiedRoot
125128
}
126129

127130
public get identifiedTasks(): ReadonlyMap<string, Identified<ast.Task>> {
@@ -148,7 +151,7 @@ class DefaultTaskListSemanticDomain implements TaskListSemanticDomain {
148151
this._identifiedTasksById = new Map()
149152
this._previousIdentifiedTransitionsById = this._identifiedTransitionsById
150153
this._identifiedTransitionsById = new Map()
151-
this._identifiedRootNode = undefined
154+
this._identifiedRoot = undefined
152155
}
153156

154157
public getIdentifiedNode(id: string): Identified<AstNode | ArtificialAstNode> | undefined {

0 commit comments

Comments
 (0)