UNPKG

langium-cli

Version:

CLI for Langium - the language engineering tool

94 lines (84 loc) 3.91 kB
/****************************************************************************** * Copyright 2023 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 { GrammarAST, type Grammar, GrammarUtils, RegExpUtils } from 'langium'; import { expandToNode, joinToNode, toString, type Generated } from 'langium/generate'; import type { LangiumLanguageConfig } from '../../package-types.js'; import { collectKeywords } from '../langium-util.js'; interface HighlightElement { pattern: string; greedy?: boolean; } type PrismHighlighter = Record<string, HighlightElement | HighlightElement[]>; const idRegex = /^[a-zA-Z_]+$/; export function generatePrismHighlighting(grammar: Grammar, config: LangiumLanguageConfig): string { const highlighter: PrismHighlighter = {}; const keywords = collectKeywords(grammar); const terminals = getTerminals(grammar); const modifier = config.caseInsensitive ? 'i' : ''; const commentTerminals = terminals.filter(GrammarUtils.isCommentTerminal); if (commentTerminals.length === 1) { highlighter.comment = { pattern: GrammarUtils.terminalRegex(commentTerminals[0]).toString(), greedy: true }; } else if (commentTerminals.length > 0) { highlighter.comment = commentTerminals.map(e => ({ pattern: GrammarUtils.terminalRegex(e).toString(), greedy: true })); } const stringTerminal = terminals.find(e => e.name.toLowerCase() === 'string'); if (stringTerminal) { highlighter.string = { pattern: GrammarUtils.terminalRegex(stringTerminal).toString(), greedy: true }; } const filteredKeywords = keywords.filter(e => idRegex.test(e)).sort((a, b) => b.length - a.length).map(RegExpUtils.escapeRegExp); highlighter.keyword = { pattern: `/\\b(${filteredKeywords.join('|')})\\b/${modifier}` }; return generate(highlighter, config.id); } function generate(highlighter: PrismHighlighter, languageId: string): string { return toString( expandToNode` // This file is generated using a best effort guess for your language. // It is not guaranteed contain all expected prism syntax highlighting rules. // For more documentation, take a look at https://prismjs.com/extending.html' Prism.languages["${languageId}"] = { ${joinToNode( Object.entries(highlighter), ([name, value]) => { const propertyName = !idRegex.test(name) ? `"${name}"` : name; return Array.isArray(value) ? expandToNode` ${propertyName}: [ ${joinToNode(value, generateElement, { separator: ',', appendNewLineIfNotEmpty: true })} ] ` : expandToNode` ${propertyName}: ${generateElement(value)} `; }, { separator: ',', appendNewLineIfNotEmpty: true } )} }; `.appendNewLine() ); } function generateElement(element: HighlightElement): Generated { const props = [ `pattern: ${element.pattern}`, element.greedy ? 'greedy: true' : undefined ].filter(Boolean); return expandToNode` { ${joinToNode(props, { separator: ',', appendNewLineIfNotEmpty: true })} } `; } function getTerminals(grammar: Grammar): GrammarAST.TerminalRule[] { return grammar.rules.filter(GrammarAST.isTerminalRule); }