Skip to content

Commit

Permalink
Merge branch 'langium-client'
Browse files Browse the repository at this point in the history
  • Loading branch information
kaisalmen committed Jul 28, 2023
2 parents fb941be + d3d5e0d commit 22e48d0
Show file tree
Hide file tree
Showing 17 changed files with 886 additions and 17 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ There are a couple of different examples that demonstrate how the `monaco-langua

- The **client** example located in [./packages/examples/main/src/client](./packages/examples/main/src/client) contains the [client web app](./packages/examples/main/src/client/main.ts) which connects to the language server therefore requires the node server app to be run in parallel.

- The **langium-web-worker-language-server** example located in [./packages/examples/main/src/langium](./packages/examples/main/src/langium) contains both the [language client](./packages/examples/main/src/langium/main.ts) and the [langauge server implementation running in a web worker](https://github.com/langium/langium/blob/main/examples/statemachine/src/language-server/main-browser.ts). They communicate via `vscode-languageserver-protocol/browser` instead of a web socket used in the **server/client** examples.
- Langium grammar language **langium-web-worker-language-server** example located in [./packages/examples/main/src/langium](./packages/examples/main/src/langium) contains both the [language client](./packages/examples/main/src/langium/langiumClient.ts) and the [langauge server (web worker)](./packages/examples/main/src/langium/langiumServerWorker.ts).

- Statemachine DSL **statemachine-web-worker-language-server** example located in [./packages/examples/main/src/langium](./packages/examples/main/src/langium) contains both the [language client](./packages/examples/main/src/langium/statemachineClient.ts) and the [langauge server (web worker)](https://github.com/langium/langium/blob/main/examples/statemachine/src/language-server/main-browser.ts).

Both web worker examples communicate via `vscode-languageserver-protocol/browser` instead of a web socket used in the **server/client** examples.

- The **browser** example located in [./packages/examples/main/src/browser](./packages/examples/main/src/browser) demonstrates how a [language service written in JavaScript](./packages/examples/main/src/browser/main.ts) can be used in a Monaco Editor contained in a simple HTML page. This example can now be considered legacy as the web worker option eases client side language server implementation and separation.

Expand Down
4 changes: 3 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ <h1>Examples</h1>
Please execute <b><code>npm run start:example:server</code></b> beforehand:<br>
<a href="packages/examples/main/client.html">Web Client for Node.js Language Server</a>
<br><br>
<a href="packages/examples/main/langium_wwls.html">Web Client & Langium Web Worker Language Server Example</a>
<a href="packages/examples/main/langium_client.html">Web Client & Langium LS (Web Worker)</a>
<br><br>
<a href="packages/examples/main/statemachine_client.html">Web Client & Statemachine LS (Web Worker)</a>
<br><br>
<a href="packages/examples/main/browser.html">Browser Example</a>
<br><br>
Expand Down
12 changes: 12 additions & 0 deletions packages/client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// this file is required for VSCode to work properly
{
"extends": "./tsconfig.src.json",
"compilerOptions": {
"noEmit": true,
"rootDir": "."
},
"include": [
"src/**/*",
"test/**/*"
]
}
17 changes: 17 additions & 0 deletions packages/examples/main/langium_client.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>Client & Langium Grammar DSL Web Worker Language Server Example</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>
<h2>Client & Langium Grammar DSL Web Worker Language Server Example</h2>
<div id="container" style="width:800px;height:600px;border:1px solid grey"></div>
<script type="module" src="./src/langium/langiumClient.ts"></script>
<div id="panel"></div>
</body>

</html>
10 changes: 5 additions & 5 deletions packages/examples/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@
"clean": "shx rm -fr dist *.tsbuildinfo",
"compile": "tsc --build tsconfig.src.json",
"build:msg": "echo Building main examples:",
"build:worker:vite": "vite --config vite.langium-worker.ts build",
"build:worker": "esbuild ../../../node_modules/langium-statemachine-dsl/out/language-server/main-browser.js --bundle --tree-shaking=true --minify --format=iife --outfile=./dist/worker/statemachineServerWorker.js",
"build": "npm run build:msg && npm run clean && npm run compile && npm run build:worker",
"build:worker:vite": "vite --config vite.statemachine-worker.ts build",
"build:worker:statemachine": "esbuild ../../../node_modules/langium-statemachine-dsl/out/language-server/main-browser.js --bundle --tree-shaking=true --minify --format=iife --outfile=./dist/worker/statemachineServerWorker.js",
"build:worker:langium": "esbuild ./src/langium/langiumServerWorker.js --bundle --tree-shaking=true --minify --format=iife --outfile=./dist/worker/langiumServerWorker.js",
"build": "npm run build:msg && npm run clean && npm run compile && npm run build:worker:statemachine && npm run build:worker:langium",
"start": "node --loader ts-node/esm src/server/main.ts",
"start:ext": "node --loader ts-node/esm src/server/main.ts --external",
"fetch:themes": "node resources/fetchThemes.mjs"
"start:ext": "node --loader ts-node/esm src/server/main.ts --external"
}
}
215 changes: 215 additions & 0 deletions packages/examples/main/src/langium/example.langium
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/******************************************************************************
* 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.
******************************************************************************/
grammar LangiumGrammar

entry Grammar:
(
isDeclared?='grammar' name=ID ('with' usedGrammars+=[Grammar:ID] (',' usedGrammars+=[Grammar:ID])*)?
(definesHiddenTokens?='hidden' '(' (hiddenTokens+=[AbstractRule:ID] (',' hiddenTokens+=[AbstractRule:ID])*)? ')')?
)?
imports+=GrammarImport*
(rules+=AbstractRule | interfaces+=Interface | types+=Type)+;

Interface:
'interface' name=ID
('extends' superTypes+=[AbstractType:ID] (',' superTypes+=[AbstractType:ID])*)?
SchemaType;

fragment SchemaType:
'{' attributes+=TypeAttribute* '}' ';'?;

TypeAttribute:
name=FeatureName (isOptional?='?')? ':' type=TypeDefinition ';'?;

TypeDefinition: UnionType;

UnionType infers TypeDefinition:
ArrayType ({infer UnionType.types+=current} ('|' types+=ArrayType)+)?;

ArrayType infers TypeDefinition:
ReferenceType ({infer ArrayType.elementType=current} '[' ']')? ;

ReferenceType infers TypeDefinition:
SimpleType |
{infer ReferenceType} '@' referenceType=SimpleType;

SimpleType infers TypeDefinition:
'(' TypeDefinition ')' |
{infer SimpleType} (typeRef=[AbstractType:ID] | primitiveType=PrimitiveType | stringType=STRING);

PrimitiveType returns string:
'string' | 'number' | 'boolean' | 'Date' | 'bigint';

type AbstractType = Interface | Type | Action | ParserRule;

Type:
'type' name=ID '=' type=TypeDefinition ';'?;

AbstractRule:
ParserRule | TerminalRule;

GrammarImport:
'import' path=STRING ';'?;

ParserRule:
(entry?='entry' | fragment?='fragment')?
RuleNameAndParams
(wildcard?='*' | ('returns' (returnType=[AbstractType:ID] | dataType=PrimitiveType)) | inferredType=InferredType<false>)?
(definesHiddenTokens?='hidden' '(' (hiddenTokens+=[AbstractRule:ID] (',' hiddenTokens+=[AbstractRule:ID])*)? ')')? ':'
definition=Alternatives ';';

InferredType<imperative>:
(<imperative> 'infer' | <!imperative> 'infers') name=ID;

fragment RuleNameAndParams:
name=ID ('<' (parameters+=Parameter (',' parameters+=Parameter)*)? '>')?;

Parameter:
name=ID;

Alternatives infers AbstractElement:
ConditionalBranch ({infer Alternatives.elements+=current} ('|' elements+=ConditionalBranch)+)?;

ConditionalBranch infers AbstractElement:
UnorderedGroup
| {infer Group} '<' guardCondition=Disjunction '>' (elements+=AbstractToken)+;

UnorderedGroup infers AbstractElement:
Group ({infer UnorderedGroup.elements+=current} ('&' elements+=Group)+)?;

Group infers AbstractElement:
AbstractToken ({infer Group.elements+=current} elements+=AbstractToken+)?;

AbstractToken infers AbstractElement:
AbstractTokenWithCardinality |
Action;

AbstractTokenWithCardinality infers AbstractElement:
(Assignment | AbstractTerminal) cardinality=('?'|'*'|'+')?;

Action infers AbstractElement:
{infer Action} '{' (type=[AbstractType:ID] | inferredType=InferredType<true>) ('.' feature=FeatureName operator=('='|'+=') 'current')? '}';

AbstractTerminal infers AbstractElement:
Keyword |
RuleCall |
ParenthesizedElement |
PredicatedKeyword |
PredicatedRuleCall |
PredicatedGroup;

Keyword:
value=STRING;

RuleCall:
rule=[AbstractRule:ID] ('<' arguments+=NamedArgument (',' arguments+=NamedArgument)* '>')?;

NamedArgument:
( parameter=[Parameter:ID] calledByName?='=')?
( value=Disjunction );

LiteralCondition:
true?='true' | 'false';

Disjunction infers Condition:
Conjunction ({infer Disjunction.left=current} '|' right=Conjunction)*;

Conjunction infers Condition:
Negation ({infer Conjunction.left=current} '&' right=Negation)*;

Negation infers Condition:
Atom | {infer Negation} '!' value=Negation;

Atom infers Condition:
ParameterReference | ParenthesizedCondition | LiteralCondition;

ParenthesizedCondition infers Condition:
'(' Disjunction ')';

ParameterReference:
parameter=[Parameter:ID];

PredicatedKeyword infers Keyword:
('=>' | '->') value=STRING;

PredicatedRuleCall infers RuleCall:
('=>' | '->') rule=[AbstractRule:ID] ('<' arguments+=NamedArgument (',' arguments+=NamedArgument)* '>')?;

Assignment infers AbstractElement:
{infer Assignment} ('=>' | '->')? feature=FeatureName operator=('+='|'='|'?=') terminal=AssignableTerminal;

AssignableTerminal infers AbstractElement:
Keyword | RuleCall | ParenthesizedAssignableElement | CrossReference;

ParenthesizedAssignableElement infers AbstractElement:
'(' AssignableAlternatives ')';

AssignableAlternatives infers AbstractElement:
AssignableTerminal ({infer Alternatives.elements+=current} ('|' elements+=AssignableTerminal)+)?;

CrossReference infers AbstractElement:
{infer CrossReference} '[' type=[AbstractType] ((deprecatedSyntax?='|' | ':') terminal=CrossReferenceableTerminal )? ']';

CrossReferenceableTerminal infers AbstractElement:
Keyword | RuleCall;

ParenthesizedElement infers AbstractElement:
'(' Alternatives ')';

PredicatedGroup infers Group:
('=>' | '->') '(' elements+=Alternatives ')';

ReturnType:
name=(PrimitiveType | ID);

TerminalRule:
hidden?='hidden'? 'terminal' (fragment?='fragment' name=ID | name=ID ('returns' type=ReturnType)?) ':'
definition=TerminalAlternatives
';';

TerminalAlternatives infers AbstractElement:
TerminalGroup ({infer TerminalAlternatives.elements+=current} '|' elements+=TerminalGroup)*;

TerminalGroup infers AbstractElement:
TerminalToken ({infer TerminalGroup.elements+=current} elements+=TerminalToken+)?;

TerminalToken infers AbstractElement:
TerminalTokenElement cardinality=('?'|'*'|'+')?;

TerminalTokenElement infers AbstractElement:
CharacterRange | TerminalRuleCall | ParenthesizedTerminalElement | NegatedToken | UntilToken | RegexToken | Wildcard;

ParenthesizedTerminalElement infers AbstractElement:
'(' (lookahead=('?='|'?!'))? TerminalAlternatives ')';

TerminalRuleCall infers AbstractElement:
{infer TerminalRuleCall} rule=[TerminalRule:ID];

NegatedToken infers AbstractElement:
{infer NegatedToken} '!' terminal=TerminalTokenElement;

UntilToken infers AbstractElement:
{infer UntilToken} '->' terminal=TerminalTokenElement;

RegexToken infers AbstractElement:
{infer RegexToken} regex=RegexLiteral;

Wildcard infers AbstractElement:
{infer Wildcard} '.';

CharacterRange infers AbstractElement:
{infer CharacterRange} left=Keyword ('..' right=Keyword)?;

FeatureName returns string:
'current' | 'entry' | 'extends' | 'false' | 'fragment' | 'grammar' | 'hidden' | 'import' | 'interface' | 'returns' | 'terminal' | 'true' | 'type' | 'infer' | 'infers' | 'with' | PrimitiveType | ID;

terminal ID: /\^?[_a-zA-Z][\w_]*/;
terminal STRING: /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/;
terminal RegexLiteral returns string: /\/(?![*+?])(?:[^\r\n\[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*\])+\//;

hidden terminal WS: /\s+/;
hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//;
hidden terminal SL_COMMENT: /\/\/[^\n\r]*/;
Loading

0 comments on commit 22e48d0

Please sign in to comment.