-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add more services to statemachine example
- add `Formatting` service - add `CodeActionProvider` service - Remove an unreachable commands, events, and states - Rename all refereed state names that don't start with caps - validate unreached commands, events, and states
- Loading branch information
1 parent
3a5a539
commit 4940b78
Showing
5 changed files
with
333 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
statemachine TrafficLight | ||
|
||
events | ||
events | ||
switchCapacity | ||
next | ||
|
||
|
151 changes: 151 additions & 0 deletions
151
examples/statemachine/src/language-server/statemachine-code-actions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
/****************************************************************************** | ||
* Copyright 2021 TypeFox GmbH | ||
* This program and the accompanying materials are made available under the | ||
* terms of the MIT License, which is available in the project root. | ||
******************************************************************************/ | ||
|
||
import type { | ||
AstNode, | ||
DiagnosticData, | ||
LangiumDocument, | ||
MaybePromise, | ||
Reference, | ||
} from 'langium'; | ||
import type { CodeActionProvider } from 'langium/lsp'; | ||
import type { | ||
CodeActionParams, | ||
Command, | ||
CodeAction, | ||
Diagnostic, | ||
TextEdit, | ||
Range, | ||
Position, | ||
} from 'vscode-languageserver'; | ||
import { CodeActionKind } from 'vscode-languageserver'; | ||
import { IssueCodes } from './statemachine-validator.js'; | ||
import type { State, Statemachine } from './generated/ast.js'; | ||
|
||
export class StatemachineCodeActionProvider implements CodeActionProvider { | ||
getCodeActions( | ||
document: LangiumDocument<AstNode>, | ||
params: CodeActionParams | ||
): MaybePromise<Array<Command | CodeAction> | undefined> { | ||
const result: CodeAction[] = []; | ||
const acceptor = (ca: CodeAction | undefined) => ca && result.push(ca); | ||
for (const diagnostic of params.context.diagnostics) { | ||
this.createCodeActions(diagnostic, document, acceptor); | ||
} | ||
return result; | ||
} | ||
|
||
private createCodeActions( | ||
diagnostic: Diagnostic, | ||
document: LangiumDocument, | ||
accept: (ca: CodeAction | undefined) => void | ||
): void { | ||
switch ((diagnostic.data as DiagnosticData)?.code) { | ||
case IssueCodes.StateNameUppercase: | ||
accept(this.makeUpperCase(diagnostic, document)); | ||
break; | ||
case IssueCodes.UnreachedState: | ||
case IssueCodes.UnreachedCommand: | ||
case IssueCodes.UnreachedEvent: | ||
accept(this.removeUnusedSymbol(diagnostic, document)); | ||
break; | ||
} | ||
return undefined; | ||
} | ||
|
||
private makeUpperCase( | ||
diagnostic: Diagnostic, | ||
document: LangiumDocument | ||
): CodeAction { | ||
const changes: TextEdit[] = []; | ||
|
||
const stateName = document.textDocument.getText(diagnostic.range); | ||
const { init, states } = document.parseResult.value as Statemachine; | ||
this.updateChangesForReferencedState(init, stateName, document, changes); | ||
|
||
states.forEach(({ transitions }) => { | ||
transitions.forEach(({ state }) => { | ||
this.updateChangesForReferencedState( | ||
state, | ||
stateName, | ||
document, | ||
changes | ||
); | ||
}); | ||
}); | ||
|
||
const range = this.getFirstLetterRange(diagnostic.range.start); | ||
changes.push(this.createTextEditForState(range, document)); | ||
return { | ||
title: 'First letter to upper case', | ||
kind: CodeActionKind.QuickFix, | ||
diagnostics: [diagnostic], | ||
isPreferred: true, | ||
edit: { | ||
changes: { | ||
[document.textDocument.uri]: changes, | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
private createTextEditForState( | ||
range: Range, | ||
document: LangiumDocument | ||
): TextEdit { | ||
const changeRange = this.getFirstLetterRange(range.start); | ||
return { | ||
range: changeRange, | ||
newText: document.textDocument.getText(changeRange).toUpperCase(), | ||
}; | ||
} | ||
|
||
private updateChangesForReferencedState( | ||
state: Reference<State>, | ||
name: string, | ||
document: LangiumDocument, | ||
changes: TextEdit[] | ||
): void { | ||
if (state.$refNode && state.ref && state.ref.name === name) { | ||
const { range } = state.$refNode; | ||
const changeRange = this.getFirstLetterRange(range.start); | ||
changes.push(this.createTextEditForState(changeRange, document)); | ||
} | ||
} | ||
|
||
private getFirstLetterRange(position: Position): Range { | ||
const range: Range = { | ||
start: position, | ||
end: { | ||
line: position.line, | ||
character: position.character + 1, | ||
}, | ||
}; | ||
return range; | ||
} | ||
|
||
private removeUnusedSymbol( | ||
diagnostic: Diagnostic, | ||
document: LangiumDocument | ||
): CodeAction { | ||
return { | ||
title: 'Remove unsed symbol', | ||
kind: CodeActionKind.QuickFix, | ||
diagnostics: [diagnostic], | ||
isPreferred: true, | ||
edit: { | ||
changes: { | ||
[document.textDocument.uri]: [ | ||
{ | ||
range: diagnostic.range, | ||
newText: '', | ||
}, | ||
], | ||
}, | ||
}, | ||
}; | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
examples/statemachine/src/language-server/statemachine-formatter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/****************************************************************************** | ||
* Copyright 2021 TypeFox GmbH | ||
* This program and the accompanying materials are made available under the | ||
* terms of the MIT License, which is available in the project root. | ||
******************************************************************************/ | ||
|
||
import type { AstNode } from 'langium'; | ||
import { AbstractFormatter, Formatting } from 'langium/lsp'; | ||
import * as ast from './generated/ast.js'; | ||
|
||
export class StatemachineFormatter extends AbstractFormatter { | ||
protected format(node: AstNode): void { | ||
if (ast.isState(node)) { | ||
const formatter = this.getNodeFormatter(node); | ||
formatter.keyword('state') | ||
.prepend(Formatting.newLine({ allowMore: true })) | ||
.append(Formatting.oneSpace()); | ||
|
||
formatter.keyword('actions').append(Formatting.oneSpace()); | ||
const bracesOpen = formatter.keyword('{'); | ||
bracesOpen.prepend(Formatting.fit(Formatting.oneSpace(), Formatting.newLine())); | ||
const bracesClose = formatter.keyword('}'); | ||
bracesClose.prepend(Formatting.newLine()); | ||
formatter.interior(bracesOpen, bracesClose).prepend(Formatting.indent()); | ||
|
||
const stateName = formatter.property('name'); | ||
const stateEnd = formatter.keyword('end'); | ||
formatter.interior(stateName, stateEnd).prepend(Formatting.indent()); | ||
stateEnd.prepend(Formatting.newLine()); | ||
} else if (ast.isStatemachine(node)) { | ||
const formatter = this.getNodeFormatter(node); | ||
|
||
formatter.keyword('statemachine').append(Formatting.oneSpace()); | ||
formatter.properties('name').append(Formatting.newLine({ allowMore: true })); | ||
|
||
formatter.keyword('initialState') | ||
.prepend(Formatting.newLine({ allowMore: true })) | ||
.append(Formatting.oneSpace()); | ||
formatter.property('init').append(Formatting.newLine({ allowMore: true })); | ||
|
||
formatter.keyword('commands') | ||
.prepend(Formatting.newLine({ allowMore: true })); | ||
formatter.keyword('events') | ||
.prepend(Formatting.newLine({ allowMore: true })); | ||
const nodes = formatter.nodes(...node.commands, ...node.events); | ||
nodes.prepend(Formatting.indent()); | ||
} else if (ast.isTransition(node)) { | ||
const formatter = this.getNodeFormatter(node); | ||
formatter.keyword('=>').surround(Formatting.oneSpace()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.