Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6f02bf7
feat: rework the service registration, rework the exposed methods
GideonKoenig Jan 20, 2025
5b5bca6
feat: rework client MessageHandler, add esbuild rule for proper packa…
GideonKoenig Jan 20, 2025
ca06be5
meta: move messageHandler
GideonKoenig Jan 20, 2025
59df6f3
fix: type issue
GideonKoenig Jan 21, 2025
09e3f53
fix: type issue
GideonKoenig Jan 21, 2025
a0cc4ec
fix: circular imports
GideonKoenig Jan 21, 2025
5e730cd
Merge branch 'custom-editor-language-server-extension' into custom-ed…
GideonKoenig Jan 21, 2025
cc01a1d
Merge branch 'main' into custom-editor-language-server-extension
GideonKoenig Jan 21, 2025
fa2cdd4
Merge branch 'main' into custom-editor-vscode-client
GideonKoenig Jan 21, 2025
c899196
style: apply automated linter fixes
megalinter-bot Jan 21, 2025
50a458e
fix: enum variants are not handled properly
GideonKoenig Jan 21, 2025
74a807d
Merge branch 'custom-editor-language-server-extension' into custom-ed…
GideonKoenig Jan 21, 2025
f327467
Merge branch 'custom-editor-vscode-client' of github.com:Safe-DS/DSL …
GideonKoenig Jan 21, 2025
1100ecb
Merge branch 'main' into custom-editor-language-server-extension
GideonKoenig Jan 22, 2025
f397f29
Merge branch 'main' into custom-editor-vscode-client
GideonKoenig Jan 22, 2025
a6d56ef
Merge branch 'main' into custom-editor-language-server-extension
GideonKoenig Mar 20, 2025
c755b43
feat: add some initial tests
GideonKoenig Mar 20, 2025
b31cd9b
feat: add some more ai generated tests
GideonKoenig Mar 21, 2025
69b8e71
feat: rework tests
GideonKoenig Mar 21, 2025
546f0c5
Merge branch 'custom-editor-language-server-extension' into custom-ed…
GideonKoenig Mar 21, 2025
b083d5c
Merge branch 'main' into custom-editor-vscode-client
GideonKoenig Mar 21, 2025
6cad99a
Merge branch 'custom-editor-vscode-client' of github.com:Safe-DS/DSL …
GideonKoenig Mar 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion packages/safe-ds-lang/src/language/communication/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { MessageDirection, NotificationType0, RequestType0 } from 'vscode-languageserver';
import { NotificationType } from 'vscode-languageserver-protocol';
import { NotificationType, RequestType } from 'vscode-languageserver-protocol';
import { UUID } from 'node:crypto';
import { Buildin, Collection } from '../graphical-editor/global.js';
import { Uri } from 'vscode';

export namespace InstallRunnerNotification {
export const method = 'runner/install' as const;
Expand Down Expand Up @@ -91,3 +93,39 @@ export namespace IsRunnerReadyRequest {
export const messageDirection = MessageDirection.clientToServer;
export const type = new RequestType0(method);
}

export namespace GraphicalEditorSyncEventNotification {
export const method = 'graphical-editor/sync-event' as const;
export const messageDirection = MessageDirection.serverToClient;
export const type = new NotificationType<Collection>(method);
}

export namespace GraphicalEditorOpenSyncChannelRequest {
export const method = 'graphical-editor/openSyncChannel' as const;
export const messageDirection = MessageDirection.clientToServer;
export const type = new RequestType<Uri, void, void>(method);
}

export namespace GraphicalEditorCloseSyncChannelRequest {
export const method = 'graphical-editor/closeSyncChannel' as const;
export const messageDirection = MessageDirection.clientToServer;
export const type = new RequestType<Uri, void, void>(method);
}

export namespace GraphicalEditorGetDocumentationRequest {
export const method = 'graphical-editor/getDocumentation' as const;
export const messageDirection = MessageDirection.clientToServer;
export const type = new RequestType<{ uri: Uri; uniquePath: string }, string | undefined, void>(method);
}

export namespace GraphicalEditorGetBuildinsRequest {
export const method = 'graphical-editor/getBuildins' as const;
export const messageDirection = MessageDirection.clientToServer;
export const type = new RequestType0<Buildin[], void>(method);
}

export namespace GraphicalEditorParseDocumentRequest {
export const method = 'graphical-editor/parseDocument' as const;
export const messageDirection = MessageDirection.clientToServer;
export const type = new RequestType<Uri, Collection, void>(method);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { isSdsLiteral, SdsArgument } from '../../generated/ast.js';
import { Call } from './call.js';
import { Placeholder } from './placeholder.js';
import { Expression, GenericExpression } from './expression.js';
import { Parameter } from './parameter.js';
import { Parser } from './parser.js';
import { CustomError } from '../types.js';

export class Argument {
constructor(
public readonly text: string,
public readonly reference: GenericExpression | Call | Placeholder | Parameter | undefined,
public readonly parameterName?: string,
) {}

public static parse(node: SdsArgument, parser: Parser) {
if (!node.value.$cstNode) return parser.pushError('CstNode missing', node.value);
const text = node.value.$cstNode.text;

let expression;
if (!isSdsLiteral(node.value)) expression = Expression.parse(node.value, parser);
if (expression instanceof CustomError) return expression;

if (node.parameter && !node.parameter.ref) return parser.pushError('Missing Parameterreference', node);
const parameterName = node.parameter?.ref?.name;

return new Argument(text, expression, parameterName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import {
SdsCall,
SdsClass,
SdsExpression,
SdsFunction,
SdsMemberAccess,
SdsPlaceholder,
SdsReference,
SdsSegment,
isSdsCall,
isSdsClass,
isSdsFunction,
isSdsMemberAccess,
isSdsPlaceholder,
isSdsReference,
isSdsSegment,
} from '../../generated/ast.js';
import { Argument } from './argument.js';
import { Edge, Port } from './edge.js';
import { GenericExpression } from './expression.js';
import { Parameter } from './parameter.js';
import { Placeholder } from './placeholder.js';
import { Result } from './result.js';
import { filterErrors } from './utils.js';
import { Parser } from './parser.js';
import { CustomError } from '../types.js';

export class Call {
private constructor(
public readonly id: number,
public readonly name: string,
public readonly self: string | undefined,
public readonly parameterList: Parameter[],
public readonly resultList: Result[],
public readonly category: string,
public readonly uniquePath: string,
) {}

public static parse(node: SdsCall, parser: Parser): Call | CustomError {
const id = parser.getNewId();

if (!isValidCallReceiver(node.receiver)) {
return parser.pushError(`Invalid Call receiver: ${debugInvalidCallReceiver(node.receiver)}`, node.receiver);
}

let name = '';
let self: string | undefined = undefined;
let category = '';
let argumentList: Argument[] = [];
let parameterList: Parameter[] = [];
let resultList: Result[] = [];

argumentList = filterErrors(node.argumentList.arguments.map((argument) => Argument.parse(argument, parser)));

if (isSdsMemberAccess(node.receiver)) {
const tmp = Call.parseSelf(node.receiver, id, parser);
if (tmp instanceof CustomError) return tmp;
self = tmp;

const functionDeclaration = node.receiver.member.target.ref;
name = functionDeclaration.name;
category = parser.getCategory(functionDeclaration)?.name ?? '';

resultList = filterErrors(
(functionDeclaration.resultList?.results ?? []).map((result) => Result.parse(result, parser)),
);
parameterList = filterErrors(
(functionDeclaration.parameterList?.parameters ?? []).map((parameter) =>
Parameter.parse(parameter, parser),
),
);
}

if (isSdsReference(node.receiver) && isSdsClass(node.receiver.target.ref)) {
const classDeclaration = node.receiver.target.ref;

name = 'new';
self = classDeclaration.name;
category = 'Modeling';

if (!classDeclaration.parameterList)
return parser.pushError('Missing constructor parameters', classDeclaration);
parameterList = filterErrors(
classDeclaration.parameterList.parameters.map((parameter) => Parameter.parse(parameter, parser)),
);
resultList = [new Result('new', classDeclaration.name)];
}

if (isSdsReference(node.receiver) && isSdsSegment(node.receiver.target.ref)) {
const segmentDeclaration = node.receiver.target.ref;

self = '';
name = segmentDeclaration.name;
category = 'Segment';

resultList = filterErrors(
(segmentDeclaration.resultList?.results ?? []).map((result) => Result.parse(result, parser)),
);
parameterList = filterErrors(
(segmentDeclaration.parameterList?.parameters ?? []).map((parameter) =>
Parameter.parse(parameter, parser),

Check warning on line 101 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L101

Added line #L101 was not covered by tests
),
);
}

const parameterListCompleted = matchArgumentsToParameter(parameterList, argumentList, node, id, parser);
if (parameterListCompleted instanceof CustomError) return parameterListCompleted;

const call = new Call(id, name, self, parameterListCompleted, resultList, category, parser.getUniquePath(node));
parser.graph.callList.push(call);
return call;
}

private static parseSelf(node: CallReceiver, id: number, parser: Parser) {
if (isSdsMemberAccess(node)) {
if (isSdsCall(node.receiver)) {
const call = Call.parse(node.receiver, parser);
if (call instanceof CustomError) return call;

Check warning on line 118 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L117-L118

Added lines #L117 - L118 were not covered by tests

if (call.resultList.length > 1) return parser.pushError('To many result', node.receiver);
if (call.resultList.length < 1) return parser.pushError('Missing result', node.receiver);

Check warning on line 121 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L120-L121

Added lines #L120 - L121 were not covered by tests

Edge.create(Port.fromResult(call.resultList[0]!, call.id), Port.fromName(id, 'self'), parser);

Check warning on line 123 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L123

Added line #L123 was not covered by tests
} else if (isSdsReference(node.receiver)) {
const receiver = node.receiver.target.ref;

if (isSdsClass(receiver)) {
return receiver.name;
} else if (isSdsPlaceholder(receiver)) {
const placeholder = Placeholder.parse(receiver, parser);
Edge.create(Port.fromPlaceholder(placeholder, false), Port.fromName(id, 'self'), parser);
}
}
}
return '';
}
}

const matchArgumentsToParameter = (
parameterList: Parameter[],
argumentList: Argument[],
callNode: SdsCall,
id: number,
parser: Parser,
): Parameter[] | CustomError => {
for (const [i, parameter] of parameterList.entries()) {
const argumentIndexMatched = argumentList[i];
if (argumentIndexMatched instanceof CustomError) return argumentIndexMatched;

const argumentNameMatched = argumentList.find(
(argument) => !(argument instanceof CustomError) && argument.parameterName === parameter.name,
) as Argument | undefined;

if (argumentIndexMatched && argumentNameMatched && argumentIndexMatched !== argumentNameMatched)
return parser.pushError(`To many matches for ${parameter.name}`, callNode.argumentList);
const argument = argumentIndexMatched ?? argumentNameMatched;

if (argument) {
parameter.argumentText = argument.text;
if (argument.reference instanceof Call) {
const call = argument.reference;
if (call.resultList.length !== 1) return parser.pushError('Type missmatch', callNode.argumentList);
Edge.create(Port.fromResult(call.resultList[0]!, call.id), Port.fromParameter(parameter, id), parser);
}

Check warning on line 164 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L161-L164

Added lines #L161 - L164 were not covered by tests
if (argument.reference instanceof GenericExpression) {
const experession = argument.reference;
Edge.create(Port.fromGenericExpression(experession, false), Port.fromParameter(parameter, id), parser);
}
if (argument.reference instanceof Placeholder) {
const placeholder = argument.reference;
Edge.create(Port.fromPlaceholder(placeholder, false), Port.fromParameter(parameter, id), parser);
}

Check warning on line 172 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L170-L172

Added lines #L170 - L172 were not covered by tests
if (argument.reference instanceof Parameter) {
const segmentParameter = argument.reference;
Edge.create(Port.fromParameter(segmentParameter, -1), Port.fromParameter(parameter, id), parser);
}
continue;
}

if (!argument && parameter.defaultValue) {
continue;
}

if (!argument && !parameter.defaultValue) {
return parser.pushError(`Missing Argument for ${parameter.name}`, callNode);
}

Check warning on line 186 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L185-L186

Added lines #L185 - L186 were not covered by tests
}
return parameterList;
};

type CallReceiver =
| (SdsReference & { target: { ref: SdsClass | SdsSegment } })
| (SdsMemberAccess & {
member: {
target: { ref: SdsFunction };
};
receiver: SdsCall | { target: { ref: SdsPlaceholder | SdsClass } };
});

const isValidCallReceiver = (receiver: SdsExpression): receiver is CallReceiver => {
/* eslint-disable no-implicit-coercion */
return (
(isSdsMemberAccess(receiver) &&
!!receiver.member &&
!!receiver.member.target.ref &&
isSdsFunction(receiver.member.target.ref) &&
((isSdsReference(receiver.receiver) &&
(isSdsClass(receiver.receiver.target.ref) || isSdsPlaceholder(receiver.receiver.target.ref))) ||
isSdsCall(receiver.receiver))) ||
(isSdsReference(receiver) && (isSdsClass(receiver.target.ref) || isSdsSegment(receiver.target.ref)))
);
};

const debugInvalidCallReceiver = (receiver: SdsExpression): string => {
/* eslint-disable no-implicit-coercion */
if (isSdsMemberAccess(receiver)) {
if (!receiver.member) return 'MemberAccess: Missing member';
if (!receiver.member.target.ref) return 'MemberAccess: Missing member declaration';
if (!isSdsFunction(receiver.member.target.ref)) return 'MemberAccess: Member is not a function';
if (!isSdsCall(receiver.receiver) && !isSdsReference(receiver.receiver))
return `MemberAccess: Receiver is not a Reference or Call but - ${receiver.receiver.$type}`;
if (
isSdsReference(receiver.receiver) &&
!isSdsClass(receiver.receiver.target.ref) &&
isSdsReference(receiver.receiver) &&
!isSdsPlaceholder(receiver.receiver.target.ref)

Check warning on line 226 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L217-L226

Added lines #L217 - L226 were not covered by tests
)
return 'MemberAccess: Reference Receiver is not Class of Placeholder';
}

Check warning on line 229 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts#L228-L229

Added lines #L228 - L229 were not covered by tests
if (isSdsReference(receiver)) {
if (!isSdsClass(receiver.target.ref) && !isSdsSegment(receiver.target.ref))
return 'Reference: Not a class or segment';
}

return receiver.$type;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { GenericExpression } from './expression.js';
import { Parameter } from './parameter.js';
import { Parser } from './parser.js';
import { Placeholder } from './placeholder.js';
import { Result } from './result.js';
import { SegmentGroupId } from './segment.js';

export class Edge {
public constructor(
public readonly from: Port,
public readonly to: Port,
) {}

public static create(from: Port, to: Port, parser: Parser) {
parser.graph.edgeList.push(new Edge(from, to));
}
}

export class Port {
private constructor(
public readonly nodeId: string,
public readonly portIdentifier: string,
) {}

public static fromName = (nodeId: number, name: string): Port => {
return new Port(nodeId.toString(), name);
};

public static fromPlaceholder = (placeholder: Placeholder, input: boolean): Port => {
return new Port(placeholder.name, input ? 'target' : 'source');
};

public static fromResult = (result: Result, nodeId: number): Port => {
return new Port(nodeId.toString(), result.name);
};

public static fromParameter = (parameter: Parameter, nodeId: number): Port => {
return new Port(nodeId.toString(), parameter.name);
};

public static fromGenericExpression(node: GenericExpression, input: boolean) {
return new Port(node.id.toString(), input ? 'target' : 'source');
}

public static fromAssignee = (node: Placeholder | Result, input: boolean): Port => {
if (node instanceof Placeholder) {
return new Port(node.name, input ? 'target' : 'source');
}
return new Port(SegmentGroupId.toString(), node.name);
};

public static isPortList(object: any): object is Port[] {
return Array.isArray(object) && object.every((element) => element instanceof Port);
}

Check warning on line 54 in packages/safe-ds-lang/src/language/graphical-editor/ast-parser/edge.ts

View check run for this annotation

Codecov / codecov/patch

packages/safe-ds-lang/src/language/graphical-editor/ast-parser/edge.ts#L53-L54

Added lines #L53 - L54 were not covered by tests
}
Loading