feat: add missing KF6 framework recipes

This commit is contained in:
2026-05-07 07:53:26 +01:00
parent d8d498f831
commit a69f479b52
2374 changed files with 2610246 additions and 0 deletions
@@ -0,0 +1 @@
Pipfile.lock
@@ -0,0 +1,12 @@
# kate: hl toml;
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
click = "~= 8.0"
jinja2 = "~= 3.0"
lxml = "*"
PyYAML = "*"
@@ -0,0 +1,501 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language
[
<!-- NOTE See https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references -->
<!ENTITY var_ref_re "[/\.\+\-_0-9A-Za-z]+">
<!-- NOTE See `cmGeneratorExpression::IsValidTargetName` -->
<!ENTITY tgt_name_re "[A-Za-z0-9_\.\+\-]+">
]>
<!--
This file is part of KDE's kate project.
SPDX-FileCopyrightText: 2004 Alexander Neundorf <neundorf@kde.org>
SPDX-FileCopyrightText: 2005 Dominik Haumann <dhdev@gmx.de>
SPDX-FileCopyrightText: 2007, 2008, 2013, 2014 Matthew Woehlke <mw_triad@users.sourceforge.net>
SPDX-FileCopyrightText: 2013-2015, 2017-2023 Alex Turbov <i.zaufi@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
-->
<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT *****
$ cd data/generators
$ ./generate-cmake-syntax.py cmake.yaml > ../syntax/cmake.xml
-->
<language
name="CMake"
version="<!--{version}-->"
kateversion="5.62"
section="Other"
extensions="CMakeLists.txt;*.cmake;*.cmake.in"
style="CMake"
mimetype="text/x-cmake"
author="Alex Turbov (i.zaufi@gmail.com)"
license="LGPLv2+"
>
<highlighting>
<list name="commands">
<!--[- for command in commands ]-->
<item><!--{command.name}--></item>
<!--[- endfor ]-->
</list>
<!--[- macro render_command_arg_lists(commands) ]-->
<!--[- for command in commands -]-->
<!--[- if command.named_args and command.named_args.kw ]-->
<list name="<!--{command.name}-->_nargs">
<!--[- for arg in command.named_args.kw ]-->
<item><!--{arg}--></item>
<!--[- endfor ]-->
</list>
<!--[- endif ]-->
<!--[- if command.special_args and command.special_args.kw ]-->
<list name="<!--{command.name}-->_sargs">
<!--[- for arg in command.special_args.kw ]-->
<item><!--{arg}--></item>
<!--[- endfor ]-->
</list>
<!--[- endif ]-->
<!--[- endfor ]-->
<!--[- endmacro ]-->
<!--{- render_command_arg_lists(commands) }-->
<!--{- render_command_arg_lists(standard_module_commands) }-->
<list name="variables">
<!--[- for var in variables.kw ]-->
<item><!--{var}--></item>
<!--[- endfor ]-->
</list>
<list name="deprecated-or-internal-variables">
<!--[- for var in deprecated_or_internal_variables.kw ]-->
<item><!--{var}--></item>
<!--[- endfor ]-->
</list>
<list name="environment-variables">
<!--[- for var in environment_variables.kw ]-->
<item><!--{var}--></item>
<!--[- endfor ]-->
</list>
<!--[- for kind in properties.kinds ]-->
<list name="<!--{ kind|replace('_', '-') }-->">
<!--[- for prop in properties[kind].kw ]-->
<item><!--{prop}--></item>
<!--[- endfor ]-->
</list>
<!--[- endfor ]-->
<list name="generator-expressions">
<!--[- for expr in generator_expressions ]-->
<item><!--{ expr }--></item>
<!--[- endfor ]-->
</list>
<!--[- for expr in complex_generator_expressions ]-->
<list name="genex-<!--{expr.name}-->-subcommands">
<!--[- for cmd in expr.subcommands ]-->
<item><!--{ cmd }--></item>
<!--[- endfor ]-->
</list>
<!--[- endfor ]-->
<list name="standard-modules">
<!--[- for module in modules.utility ]-->
<item><!--{ module }--></item>
<!--[- endfor ]-->
</list>
<list name="standard-finder-modules">
<!--[- for module in modules.finder ]-->
<item><!--{ module | replace('Find', '') }--></item>
<!--[- endfor ]-->
</list>
<list name="deprecated-modules">
<!--[- for module in modules.deprecated ]-->
<item><!--{ module }--></item>
<!--[- endfor ]-->
</list>
<!-- Source/cmStringAlgorithms.cxx: bool cmIsOff(cm::string_view val) -->
<list name="true_special_arg">
<item>TRUE</item>
<item>ON</item>
<item>YES</item>
<item>Y</item>
<item>0</item>
</list>
<!-- Source/cmStringAlgorithms.cxx: bool cmIsOff(cm::string_view val) -->
<list name="false_special_arg">
<item>FALSE</item>
<item>OFF</item>
<item>NO</item>
<item>IGNORE</item>
<item>N</item>
<item>0</item>
</list>
<contexts>
<context attribute="Normal Text" lineEndContext="#stay" name="Normal Text">
<DetectSpaces />
<!--[ for command in commands -]-->
<WordDetect String="<!--{command.name}-->" insensitive="true" attribute="<!--{command.attribute}-->" context="<!--{command.name}-->_ctx"<!--[ if command.start_region ]--> beginRegion="<!--{command.start_region}-->"<!--[ endif -]--> <!--[- if command.end_region ]--> endRegion="<!--{command.end_region}-->"<!--[ endif ]--> />
<!--[ endfor -]-->
<!--[ for command in standard_module_commands -]-->
<WordDetect String="<!--{command.name}-->" insensitive="true" attribute="CMake Provided Function/Macro" context="<!--{command.name}-->_ctx" />
<!--[ endfor -]-->
<DetectChar attribute="Comment" context="Match Comments and Docs" char="#" lookAhead="true" />
<DetectIdentifier attribute="User Function/Macro" context="User Function" />
<RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&var_ref_re;@" lookAhead="true" />
<IncludeRules context="LineError" />
</context>
<!--[- macro render_command_parsers(commands) ]-->
<!--[ for command in commands -]-->
<context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx">
<DetectChar attribute="Normal Text" context="<!--{command.name}-->_ctx_op<!--{'_tgt_first' if command.first_arg_is_target else '_tgts_first' if command.first_args_are_targets else ''}-->" char="(" />
<DetectChar attribute="Normal Text" context="#pop" char=")" />
</context>
<!--[- if command.first_arg_is_target ]-->
<context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op_tgt_first">
<DetectSpaces />
<RegExpr attribute="Aliased Targets" context="<!--{command.name}-->_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*" />
<RegExpr attribute="Targets" context="<!--{command.name}-->_ctx_op" String="&tgt_name_re;" />
<IncludeRules context="User Function Opened" />
<IncludeRules context="LineError" />
</context>
<!--[- endif ]-->
<!--[- if command.first_args_are_targets ]-->
<context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op_tgts_first">
<DetectSpaces />
<!--[- if command.named_args and command.named_args.kw ]-->
<!-- NOTE Handle the only case in CMake nowadays:
1. `set_target_properties` have a named keyword (`PROPERTIES`) after targets list
-->
<keyword context="<!--{command.name}-->_ctx_op" String="<!--{command.name}-->_nargs" lookAhead="true" />
<!--[- endif ]-->
<IncludeRules context="Detect Aliased Targets" />
<IncludeRules context="Detect Targets" />
<IncludeRules context="User Function Opened" />
<IncludeRules context="LineError" />
</context>
<!--[- endif ]-->
<!--[- if not command.first_args_are_targets or (command.named_args and command.named_args.kw) ]-->
<context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op">
<DetectSpaces />
<!--[- if command.nested_parentheses ]-->
<DetectChar attribute="Normal Text" context="<!--{command.name}-->_ctx_op_nested" char="(" />
<!--[- endif ]-->
<DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true" />
<!--[- if command.named_args and command.named_args.kw ]-->
<!--[- if command.has_target_name_after_kw ]-->
<WordDetect String="<!--{command.has_target_name_after_kw}-->" attribute="Named Args" context="Target Name" />
<!--[- endif ]-->
<!--[- if command.has_target_names_after_kw ]-->
<!--[- for kw in command.has_target_names_after_kw ]-->
<WordDetect String="<!--{kw}-->" attribute="Named Args" context="<!--{command.name}-->_tgts" />
<!--[- endfor ]-->
<!--[- endif ]-->
<keyword attribute="Named Args" context="#stay" String="<!--{command.name}-->_nargs" />
<!--[- endif ]-->
<!--[- if command.name == 'include' ]-->
<keyword attribute="Standard Module" context="#stay" String="standard-modules" />
<keyword attribute="Deprecated Module" context="#stay" String="deprecated-modules" />
<!--[- endif ]-->
<!--[- if command.name == 'find_package' ]-->
<keyword attribute="Standard Module" context="#stay" String="standard-finder-modules" />
<!--[- endif ]-->
<!--[- if command.special_args and command.special_args.kw ]-->
<keyword attribute="Special Args" context="#stay" String="<!--{command.name}-->_sargs" />
<!--[- endif ]-->
<!--[- if command.property_args and command.property_args.kw ]-->
<!--[- for kind in command.property_args.kw ]-->
<keyword attribute="Property" context="#stay" String="<!--{kind}-->" />
<!--[- if properties[kind|replace('-', '_')].re ]-->
<IncludeRules context="Detect More <!--{kind}-->" />
<!--[- endif ]-->
<!--[- endfor ]-->
<!--[- endif ]-->
<!--[- if command is not nulary ]-->
<IncludeRules context="User Function Args" />
<!--[- if command.name == 'cmake_policy' ]-->
<!-- NOTE Handle CMP<NNN> as a special arg of `cmake_policy` command -->
<RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9]+\b" />
<!--[- endif ]-->
<!--[- endif ]-->
</context>
<!--[- endif ]-->
<!--[- if command.has_target_names_after_kw ]-->
<context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_tgts">
<DetectSpaces />
<DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true" />
<keyword attribute="Named Args" context="#pop" String="<!--{command.name}-->_nargs" lookAhead="true" />
<IncludeRules context="Detect Aliased Targets" />
<IncludeRules context="Detect Targets" />
<IncludeRules context="User Function Args" />
<IncludeRules context="LineError" />
</context>
<!--[- endif ]-->
<!--[- if command.nested_parentheses ]-->
<context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op_nested">
<DetectSpaces />
<DetectChar attribute="Normal Text" context="#pop" char=")" />
<DetectChar attribute="Normal Text" context="<!--{command.name}-->_ctx_op_nested" char="(" />
<!--[- if command.named_args and command.named_args.kw ]-->
<keyword attribute="Named Args" context="#stay" String="<!--{command.name}-->_nargs" />
<!--[- endif ]-->
<!--[- if command.special_args and command.special_args.kw ]-->
<keyword attribute="Special Args" context="#stay" String="<!--{command.name}-->_sargs" />
<!--[- endif ]-->
<!--[- if command.property_args and command.property_args.kw ]-->
<!--[- for kind in command.property_args.kw ]-->
<keyword attribute="Property" context="#stay" String="<!--{kind}-->" />
<!--[- if properties[kind|replace('-', '_')].re ]-->
<IncludeRules context="Detect More <!--{kind}-->" />
<!--[- endif ]-->
<!--[- endfor ]-->
<!--[- endif ]-->
<IncludeRules context="User Function Args" />
</context>
<!--[- endif ]-->
<!--[ endfor -]-->
<!--[- endmacro -]-->
<!--{- render_command_parsers(commands) -}-->
<!--{- render_command_parsers(standard_module_commands) -}-->
<!--[ for kind in properties.kinds if properties[kind].re -]-->
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More <!--{ kind|replace('_', '-') }-->">
<RegExpr attribute="Property" context="#stay" String="<!--{properties[kind].re}-->" />
</context><!--{ '\n' }-->
<!--[ endfor -]-->
<context attribute="User Function/Macro" lineEndContext="#stay" name="User Function">
<DetectChar attribute="Normal Text" context="User Function Opened" char="(" />
<DetectChar attribute="Normal Text" context="#pop" char=")" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="User Function Opened">
<DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true" />
<IncludeRules context="User Function Args" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Builtin Variables">
<RegExpr attribute="Internal Name" context="#stay" String="\b_&var_ref_re;\b" />
<keyword attribute="CMake Internal Variable" context="#stay" String="deprecated-or-internal-variables" insensitive="false" />
<keyword attribute="Builtin Variable" context="#stay" String="variables" insensitive="false" />
<IncludeRules context="Detect More Builtin Variables" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More Builtin Variables">
<!--[- if deprecated_or_internal_variables.re ]-->
<RegExpr attribute="CMake Internal Variable" context="#stay" String="<!--{deprecated_or_internal_variables.re}-->" />
<!--[- endif ]-->
<!--[- if variables.re ]-->
<RegExpr attribute="Builtin Variable" context="#stay" String="<!--{variables.re}-->" />
<!--[- endif ]-->
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Variable Substitutions">
<RegExpr attribute="Cache Variable Substitution" context="#stay" String="\$CACHE\{\s*[\w-]+\s*\}" />
<RegExpr attribute="Environment Variable Substitution" context="EnvVarSubst" String="\$?ENV\{" />
<Detect2Chars attribute="Variable Substitution" context="VarSubst" char="$" char1="{" />
<RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&var_ref_re;@" lookAhead="true" />
</context>
<context attribute="Environment Variable Substitution" lineEndContext="#pop" name="EnvVarSubst">
<DetectChar attribute="Environment Variable Substitution" context="#pop" char="}" />
<keyword attribute="Standard Environment Variable" context="#stay" String="environment-variables" insensitive="false" />
<!--[- if environment_variables.re ]-->
<RegExpr attribute="Standard Environment Variable" context="#stay" String="<!--{environment_variables.re}-->" />
<!--[- endif ]-->
<DetectIdentifier />
<IncludeRules context="Detect Variable Substitutions" />
</context>
<context attribute="Variable Substitution" lineEndContext="#pop" name="VarSubst">
<DetectChar attribute="Variable Substitution" context="#pop" char="}" />
<IncludeRules context="Detect Builtin Variables" />
<DetectIdentifier />
<IncludeRules context="Detect Variable Substitutions" />
</context>
<context attribute="@Variable Substitution" lineEndContext="#pop" name="@VarSubst">
<DetectChar attribute="@Variable Substitution" context="VarSubst@" char="@" />
</context>
<context attribute="@Variable Substitution" lineEndContext="#pop#pop" name="VarSubst@">
<DetectChar attribute="@Variable Substitution" context="#pop#pop" char="@" />
<IncludeRules context="Detect Builtin Variables" />
<DetectIdentifier />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Target Name">
<DetectSpaces />
<RegExpr attribute="Aliased Targets" context="#pop" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*" />
<IncludeRules context="Detect Targets" />
<IncludeRules context="User Function Opened" />
<IncludeRules context="LineError" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Targets">
<RegExpr attribute="Targets" context="#stay" String="&tgt_name_re;" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="LineError">
<RegExpr attribute="Error" context="#stay" String=".*" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="User Function Args">
<Detect2Chars attribute="Normal Text" context="#stay" char="\" char1="(" />
<Detect2Chars attribute="Normal Text" context="#stay" char="\" char1=")" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="&quot;" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="$" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="n" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="\" />
<DetectChar attribute="Strings" context="String" char="&quot;" />
<RegExpr attribute="Strings" context="Bracketed String" String="\[(=*)\[" beginRegion="BracketedString" />
<DetectChar attribute="Comment" context="Match Comments" char="#" lookAhead="true" />
<IncludeRules context="Detect Builtin Variables" />
<IncludeRules context="Detect Variable Substitutions" />
<IncludeRules context="Detect Special Values" />
<IncludeRules context="Detect Aliased Targets" />
<IncludeRules context="Detect Generator Expressions" />
<DetectIdentifier />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Special Values">
<RegExpr attribute="Version Arg" context="#stay" String="\b[0-9]++(.[0-9]++)+\b" />
<keyword attribute="True Special Arg" context="#stay" String="true_special_arg" insensitive="true" />
<keyword attribute="False Special Arg" context="#stay" String="false_special_arg" insensitive="true" />
<RegExpr attribute="False Special Arg" context="#stay" String="\b(?:&var_ref_re;-)?NOTFOUND\b" />
<RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9][0-9][0-9][0-9]\b" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Aliased Targets">
<RegExpr attribute="Aliased Targets" context="#stay" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="Match Comments">
<DetectSpaces />
<RegExpr attribute="Comment" context="#pop!Bracketed Comment" String="#\[(=*)\[" beginRegion="BracketedComment" />
<DetectChar attribute="Comment" context="#pop!Comment" char="#" />
<DetectIdentifier />
</context>
<context attribute="Comment" lineEndContext="#pop" name="Match Comments and Docs">
<RegExpr attribute="Region Marker" context="#pop!RST Documentation" String="^#\[(=*)\[\.rst:" column="0" beginRegion="RSTDocumentation" />
<IncludeRules context="Match Comments" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="Comment">
<DetectSpaces />
<LineContinue attribute="Comment" context="#pop" />
<IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
<context attribute="Comment" lineEndContext="#stay" name="RST Documentation" dynamic="true">
<RegExpr attribute="Region Marker" context="#pop" String="^#?\]%1\]" dynamic="true" column="0" endRegion="RSTDocumentation" />
<IncludeRules context="##reStructuredText" />
</context>
<context attribute="Comment" lineEndContext="#stay" name="Bracketed Comment" dynamic="true">
<LineContinue attribute="Comment" context="#stay" />
<DetectSpaces />
<StringDetect attribute="Comment" context="#pop" String="]%1]" dynamic="true" endRegion="BracketedComment" />
<IncludeRules context="##Comments" />
</context>
<context attribute="Strings" lineEndContext="#stay" name="String">
<DetectSpaces />
<DetectIdentifier />
<RegExpr attribute="Strings" context="#pop" String="&quot;(?=[ );]|$)" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="&quot;" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="$" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="n" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="r" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="t" />
<Detect2Chars attribute="Escapes" context="#stay" char="\" char1="\" />
<IncludeRules context="Detect Variable Substitutions" />
<IncludeRules context="Detect Generator Expressions" />
</context>
<context attribute="Strings" lineEndContext="#stay" name="Bracketed String" dynamic="true">
<StringDetect attribute="Strings" context="#pop" String="]%1]" dynamic="true" endRegion="BracketedString" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Generator Expressions">
<Detect2Chars attribute="Generator Expression" context="Generator Expression" char="$" char1="&lt;" />
</context>
<context attribute="Generator Expression" lineEndContext="#stay" name="Generator Expression">
<IncludeRules context="Detect Generator Expressions" />
<DetectChar attribute="Comment" context="Comment" char="#" />
<DetectChar attribute="Generator Expression" context="#pop" char="&gt;" />
<keyword attribute="Generator Expression Keyword" context="#stay" String="generator-expressions" insensitive="false" />
<!--[- for expr in complex_generator_expressions ]-->
<WordDetect String="<!--{expr.name}-->" attribute="Generator Expression Keyword" context="genex_<!--{expr.name}-->_ctx" />
<!--[- endfor ]-->
<IncludeRules context="Detect Aliased Targets" />
<IncludeRules context="Detect Variable Substitutions" />
<DetectIdentifier />
</context>
<!--[- for expr in complex_generator_expressions ]-->
<context attribute="Generator Expression" lineEndContext="#stay" name="genex_<!--{expr.name}-->_ctx" fallthroughContext="#pop">
<DetectChar char=":" context="#stay" />
<DetectSpaces />
<keyword attribute="Generator Expression Sub-Command" context="#pop" String="genex-<!--{expr.name}-->-subcommands" insensitive="false" />
</context>
<!--[- endfor ]-->
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Comment" defStyleNum="dsComment" spellChecking="true" />
<itemData name="Command" defStyleNum="dsKeyword" spellChecking="false" />
<itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false" />
<itemData name="CMake Provided Function/Macro" defStyleNum="dsFunction" bold="true" spellChecking="false" />
<itemData name="User Function/Macro" defStyleNum="dsFunction" spellChecking="false" />
<itemData name="Property" defStyleNum="dsOthers" spellChecking="false" />
<itemData name="Targets" defStyleNum="dsBaseN" spellChecking="false" />
<itemData name="Aliased Targets" defStyleNum="dsBaseN" spellChecking="false" />
<itemData name="Named Args" defStyleNum="dsOthers" spellChecking="false" />
<itemData name="Special Args" defStyleNum="dsOthers" spellChecking="false" />
<itemData name="True Special Arg" defStyleNum="dsOthers" color="#30a030" selColor="#30a030" spellChecking="false" />
<itemData name="False Special Arg" defStyleNum="dsOthers" color="#e05050" selColor="#e05050" spellChecking="false" />
<itemData name="Version Arg" defStyleNum="dsDataType" spellChecking="false" />
<itemData name="Strings" defStyleNum="dsString" spellChecking="true" />
<itemData name="Escapes" defStyleNum="dsSpecialChar" spellChecking="false" />
<itemData name="Builtin Variable" defStyleNum="dsDecVal" color="#c09050" selColor="#c09050" spellChecking="false" />
<itemData name="CMake Internal Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Internal Name" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Variable Substitution" defStyleNum="dsDecVal" spellChecking="false" />
<itemData name="@Variable Substitution" defStyleNum="dsBaseN" spellChecking="false" />
<itemData name="Cache Variable Substitution" defStyleNum="dsFloat" spellChecking="false" />
<itemData name="Environment Variable Substitution" defStyleNum="dsFloat" spellChecking="false" />
<itemData name="Standard Environment Variable" defStyleNum="dsFloat" spellChecking="false" />
<itemData name="Generator Expression Keyword" defStyleNum="dsKeyword" color="#b84040" selColor="#b84040" spellChecking="false" />
<itemData name="Generator Expression Sub-Command" defStyleNum="dsKeyword" color="#c05050" selColor="#c05050" spellChecking="false" />
<itemData name="Generator Expression" defStyleNum="dsOthers" color="#b86050" selColor="#b86050" spellChecking="false" />
<itemData name="Standard Module" defStyleNum="dsImport" spellChecking="false" />
<itemData name="Deprecated Module" defStyleNum="dsImport" spellChecking="false" />
<itemData name="Region Marker" defStyleNum="dsRegionMarker" spellChecking="false" />
<itemData name="Error" defStyleNum="dsError" spellChecking="false" />
</itemDatas>
</highlighting>
<general>
<comments>
<comment name="singleLine" start="#" position="afterwhitespace" />
<comment name="multiLine" start="#[[" end="]]" region="BracketedComment" />
</comments>
<keywords casesensitive="1" weakDeliminator="." />
</general>
</language>
<!-- kate: replace-tabs on; indent-width 2; tab-width 2; -->
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,516 @@
#!/usr/bin/env python3
#
# Generate Kate syntax file for CMake
#
# SPDX-FileCopyrightText: 2017-2024 Alex Turbov <i.zaufi@gmail.com>
#
# To install prerequisites:
#
# $ pip install --user click jinja2 lxml pyyaml
#
# To use:
#
# $ ./generate-cmake-syntax.py cmake.yaml > ../syntax/cmake.xml
#
from __future__ import annotations
import functools
import re
from dataclasses import dataclass, field
import click
import jinja2
import yaml
import sys
from lxml import etree
_TEMPLATED_NAME = re.compile(r'(?:<[^>]+>)')
_PROPERTY_KEYS = [
'global-properties'
, 'directory-properties'
, 'target-properties'
, 'source-properties'
, 'test-properties'
, 'cache-properties'
, 'install-properties'
]
_KW_RE_LIST = ['kw', 're']
_VAR_KIND_LIST = ['variables', 'deprecated-or-internal-variables', 'environment-variables']
_CONTROL_FLOW_LIST = {
'break'
, 'continue'
, 'elseif'
, 'else'
, 'endforeach'
, 'endif'
, 'endwhile'
, 'foreach'
, 'if'
, 'return'
, 'while'
}
_VAR_REF_ENTITY = '&var_ref_re;'
_HEURISTICS = [
(
{'MAX(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?', 'MIN(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?'}
, 'M(AX|IN)(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?'
)
, ({'OUTPUTS', 'OUTPUT_(HEADER|SOURCE)'}, 'OUTPUT(S|_(HEADER|SOURCE))')
, ({'PREFIX', 'SUFFIX'}, '(PRE|SUF)FIX')
, ({'CPPCHECK', 'CPPLINT'}, 'CPP(CHECK|LINT)')
, ({'DEPENDS', 'PREDEPENDS'}, '(PRE)?DEPENDS')
, ({'ICON', 'ICONURL'}, 'ICON(URL)?')
, (
{
'&var%ref%re;(_INIT)?'
, 'DEBUG(_INIT)?'
, 'MINSIZEREL(_INIT)?'
, 'RELEASE(_INIT)?'
, 'RELWITHDEBINFO(_INIT)?'
}
, '(DEBUG|MINSIZEREL|REL(EASE|WITHDEBINFO)|&var%ref%re;)(_INIT)?'
)
, ({'RELEASE', 'RELWITHDEBINFO'}, 'REL(EASE|WITHDEBINFO)')
, ({'POST', 'POSTUN', 'PRE', 'PREUN'}, 'P(RE|OST)(UN)?')
, ({'AUTOPROV', 'AUTOREQ', 'AUTOREQPROV'}, 'AUTO(PROV|REQ(PROV)?)')
, ({'DEFINITIONS', 'OPTIONS'}, '(DEFINI|OP)TIONS')
, ({'LIB_NAMES', 'LIBRARY'}, 'LIB(_NAMES|RARY)')
, ({'EXTENSIONS', 'EXTRA_FLAGS'}, 'EXT(ENSIONS|RA_FLAGS)')
, ({'DISABLED', 'DISPLAY_NAME'}, 'DIS(ABLED|PLAY_NAME)')
, ({'LIBRARIES', 'LINK_LIBRARIES', 'STATIC_LINK_LIBRARIES'}, '((STATIC_)?LINK_)?LIBRARIES')
, ({'INCLUDE_DIRS', 'LIBRARY_DIRS'}, '(INCLUDE|LIBRARY)_DIRS')
, ({'BINARY_DIR', 'SOURCE_DIR'}, '(BINARY|SOURCE)_DIR')
, ({'CFLAGS(_OTHER)?', 'LDFLAGS(_OTHER)?'}, '(C|LD)FLAGS(_OTHER)?')
, ({'INCLUDE_DIRECTORIES', 'LIBRARIES'}, '(INCLUDE_DIRECTO|LIBRA)RIES')
, ({'POSTFLIGHT_&var%ref%re;_SCRIPT', 'PREFLIGHT_&var%ref%re;_SCRIPT'}, 'P(RE|OST)FLIGHT_&var%ref%re;_SCRIPT')
, ({'DIRECTORIES', 'FRAMEWORK_DIRECTORIES'}, '(FRAMEWORK_)?DIRECTORIES')
, ({'FILE_FLAG', 'FILE'}, 'FILE(_FLAG)?')
, ({'DIR_PERMISSIONS', 'FILE_PERMISSIONS'}, '(DIR|FILE)_PERMISSIONS')
, ({'COMPILER_LAUNCHER', 'LINKER_LAUNCHER'}, '(COMPIL|LINK)ER_LAUNCHER')
, ({'COMPILER', 'COMPILE_(DEFINI|OP)TIONS'}, 'COMPILE(R|_(DEFINI|OP)TIONS)')
, ({'LICENSEURL', 'LICENSE_(EXPRESSION|FILE_NAME)'}, 'LICENSE(URL|_(EXPRESSION|FILE_NAME))')
, ({'NO_SONAME', 'SONAME'}, '(NO_)?SONAME')
, ({'CODE_SIGN_ON_COPY', 'REMOVE_HEADERS_ON_COPY'}, '(CODE_SIGN|REMOVE_HEADERS)_ON_COPY')
, ({'(REFERENCE|REFERENCEPROP_&var%ref%re;_TAG)_&var%ref%re;'}, 'REFERENCE(PROP_&var%ref%re;_TAG)?_&var%ref%re;')
, ({'DISABLE_FIND_PACKAGE', 'REQUIRE_FIND_PACKAGE'}, '(DISABLE|REQUIRE)_FIND_PACKAGE')
, (
{'GROUP_USING_&var%ref%re;(_SUPPORTED)?', 'LIBRARY_USING_&var%ref%re;(_SUPPORTED)?'}
, '(GROUP|LIBRARY)_USING_&var%ref%re;(_SUPPORTED)?'
)
, (
{
'EXE_LINKER_FLAGS_&var%ref%re;(_INIT)?'
, 'MODULE_LINKER_FLAGS_&var%ref%re;(_INIT)?'
, 'SHARED_LINKER_FLAGS_&var%ref%re;(_INIT)?'
, 'STATIC_LINKER_FLAGS_&var%ref%re;(_INIT)?'
}
, '(EXE|MODULE|SHARED|STATIC)_LINKER_FLAGS_&var%ref%re;(_INIT)?'
)
, (
{
'ARCHIVE_OUTPUT_DIRECTORY'
, 'COMPILE_PDB_OUTPUT_DIRECTORY'
, 'LIBRARY_OUTPUT_DIRECTORY'
, 'PDB_OUTPUT_DIRECTORY'
, 'RUNTIME_OUTPUT_DIRECTORY'
}
, '(ARCHIVE|(COMPILE_)?PDB|LIBRARY|RUNTIME)_OUTPUT_DIRECTORY'
)
, (
{
'ARCHIVE_OUTPUT_(DIRECTORY|NAME)'
, 'LIBRARY_OUTPUT_(DIRECTORY|NAME)'
, 'RUNTIME_OUTPUT_(DIRECTORY|NAME)'
}
, '(ARCHIVE|LIBRARY|RUNTIME)_OUTPUT_(DIRECTORY|NAME)'
)
, ({'ASM&var_ref_re;', 'ASM&var_ref_re;FLAGS'}, 'ASM&var_ref_re;(FLAGS)?')
, (
{
'CMAKE_POLICY_DEFAULT_CMP[0-9]{4}'
, 'CMAKE_POLICY_WARNING_CMP[0-9]{4}'
}
, 'CMAKE_POLICY_(DEFAULT|WARNING)_CMP[0-9]{4}'
)
, ({'CMAKE_ARGV[0-9]+', 'CMAKE_MATCH_[0-9]+'}, 'CMAKE_(ARGV|MATCH_)[0-9]+')
]
@dataclass
class RePartNode:
children: dict[str, RePartNode] = field(default_factory=dict, hash=False)
is_leaf: bool = False
@dataclass
class RegexCollection:
special_cases: list[str] = field(default_factory=list, hash=False)
re_tree: dict[str, RePartNode] = field(default_factory=dict, hash=False)
def add_case(self, regex: str) -> RegexCollection:
self.special_cases.append(regex)
return self
def update_tree(self, name_parts: list[str]) -> RegexCollection:
safe_var_ref = _VAR_REF_ENTITY.replace('_', '%')
functools.reduce(
lambda current, part: (
self.re_tree if current is None else current.children
).setdefault(part, RePartNode())
, (
safe_var_ref
.join(name_parts)
.replace(f'{safe_var_ref}_{safe_var_ref}', safe_var_ref)
.split('_')
)
, None
).is_leaf = True
return self
def try_transform_placeholder_string_to_regex(state: RegexCollection, name: str):
'''
NOTE Some placeholders are not IDs, but numbers...
`CMAKE_MATCH_<N>` 4 example
'''
name_parts = _TEMPLATED_NAME.split(name)
match name_parts:
case ['CMAKE_MATCH_' as head, ''] | ['CMAKE_ARGV' as head, ''] | ['ARGV' as head, '']:
return state.add_case(head + '[0-9]+')
case ['CMAKE_POLICY_DEFAULT_CMP' as head, ''] | ['CMAKE_POLICY_WARNING_CMP' as head, '']:
return state.add_case(head + '[0-9]{4}')
case ['', '__TRYRUN_OUTPUT']:
return state.add_case(f'{_VAR_REF_ENTITY}__TRYRUN_OUTPUT')
case (['ASM', ''] | ['ASM', 'FLAGS']) as asm_env:
return state.add_case(f'{asm_env[0]}{_VAR_REF_ENTITY}{asm_env[1]}')
return state.update_tree(name_parts)
def is_first_subset_of_second(first, second):
subset = set(first)
fullset = set(second)
return subset.issubset(fullset)
def try_optimize_known_alt_groups(groups: list[str]) -> list[str]:
for case in _HEURISTICS:
if is_first_subset_of_second(case[0], groups):
groups = sorted([*filter(lambda item: item not in case[0], groups), case[1]])
return groups
def try_optimize_trailing_var_ref_regex(groups: list[str]) -> list[str]:
tail_var_ref_re = '_' + _VAR_REF_ENTITY.replace('_', '%')
candidates = [*filter(lambda s: s.endswith(tail_var_ref_re), groups)]
return sorted([
*filter(lambda item: item not in candidates, groups)
, f'({"|".join(try_optimize_known_alt_groups([s[:-len(tail_var_ref_re)] for s in candidates]))}){tail_var_ref_re}'
]) if len(candidates) > 1 else groups
def build_regex(state: list[str], kv: tuple[str, RePartNode]) -> list[str]:
name, value = kv
match (value, len(value.children)):
case (RePartNode(children={}, is_leaf=True), 0):
return [*state, name]
case (node, sz) if sz > 0:
alt_group = try_optimize_known_alt_groups(
try_optimize_trailing_var_ref_regex(
functools.reduce(build_regex, node.children.items(), [])
)
)
match (len(alt_group), node.is_leaf):
case (1, False):
return [*state, f'{name}_{alt_group[0]}']
case (1, True):
return [*state, f'{name}(_{alt_group[0]})?']
case (sz, False) if sz > 0:
return [*state, f'{name}_({"|".join(alt_group)})']
case (sz, True) if sz > 0:
return [*state, f'{name}(_({"|".join(alt_group)}))?']
case _:
raise AssertionError('Zero children?')
case _:
raise AssertionError(f'NOT MATCHED: {name=}{value=}')
return state
def try_placeholders_to_regex(names):
if not names:
return None
data = functools.reduce(
try_transform_placeholder_string_to_regex
, names
, RegexCollection()
)
return (
'\\b(?:'
+ '|'.join(
try_optimize_known_alt_groups(
try_optimize_trailing_var_ref_regex(
functools.reduce(
build_regex
, data.re_tree.items()
, data.special_cases
)
)
)
).replace('%', '_')
+ ')\\b'
)
def partition_iterable(fn, iterable):
true, false = [], []
for i in iterable:
(false, true)[int(fn(i))].append(i)
return true, false
def _transform_command_set(cmd, list_name):
args, args_re = partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, cmd[list_name])
del cmd[list_name]
list_name = list_name.replace('-', '_')
cmd[list_name] = {k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [args, args_re])}
cmd[list_name]['re'] = try_placeholders_to_regex(args_re)
return cmd
def transform_command(cmd):
can_be_nulary = True
if 'name' not in cmd:
raise RuntimeError('Command have no name')
if 'named-args' in cmd:
new_cmd = _transform_command_set(cmd, 'named-args')
assert new_cmd == cmd
can_be_nulary = False
if 'special-args' in cmd:
new_cmd = _transform_command_set(cmd, 'special-args')
assert new_cmd == cmd
can_be_nulary = False
if 'property-args' in cmd:
new_cmd = _transform_command_set(cmd, 'property-args')
assert new_cmd == cmd
can_be_nulary = False
cmd['nested_parentheses'] = cmd.get('nested-parentheses?', False)
if 'first-arg-is-target?' in cmd:
cmd['first_arg_is_target'] = cmd['first-arg-is-target?']
can_be_nulary = False
if 'first-args-are-targets?' in cmd:
cmd['first_args_are_targets'] = cmd['first-args-are-targets?']
can_be_nulary = False
if 'has-target-name-after-kw' in cmd:
cmd['has_target_name_after_kw'] = cmd['has-target-name-after-kw']
can_be_nulary = False
if 'has-target-names-after-kw' in cmd:
match cmd['has-target-names-after-kw']:
case str():
cmd['has_target_names_after_kw'] = [cmd['has-target-names-after-kw']]
case list():
cmd['has_target_names_after_kw'] = cmd['has-target-names-after-kw']
case _:
raise TypeError('Unexpected type for `has-target-names-after-kw`')
can_be_nulary = False
if 'second-arg-is-target?' in cmd:
cmd['second_arg_is_target'] = cmd['second-arg-is-target?']
can_be_nulary = False
if 'nulary?' in cmd and cmd['nulary?'] and not can_be_nulary:
raise RuntimeError('Command `{}` w/ args declared nulary!?'.format(cmd['name']))
if 'start-region' in cmd:
cmd['start_region'] = cmd['start-region']
if 'end-region' in cmd:
cmd['end_region'] = cmd['end-region']
cmd['attribute'] = 'Control Flow' if cmd['name'] in _CONTROL_FLOW_LIST else 'Command'
return cmd
def remove_duplicate_list_nodes(root):
remap = {}
items_by_kws = {}
# extract duplicate keyword list
for items in root.iterfind('highlighting/list'):
key = '<'.join(item.text for item in items)
name = items.attrib['name']
if rename := items_by_kws.get(key):
remap[name] = rename
items.getparent().remove(items)
else:
items_by_kws[key] = name
# update keyword list name referenced by each rule
for rule in root.iterfind('highlighting/contexts/context/keyword'):
name = rule.attrib['String']
rule.attrib['String'] = remap.get(name, name)
def remove_duplicate_context_nodes(root):
contexts = root[0].find('contexts')
# 3 levels: ctx, ctx_op and ctx_op_nested
# TODO Refactor it!
for _ in range(3):
remap = {}
duplicated = {}
# remove duplicate nodes
for context in contexts:
name = context.attrib['name']
context.attrib['name'] = 'dummy'
ref = duplicated.setdefault(etree.tostring(context), [])
if ref:
contexts.remove(context)
else:
context.attrib['name'] = name
ref.append(name)
remap[name] = ref[0]
# update context name referenced by each rule
for context in contexts:
for rule in context:
ref = remap.get(rule.attrib.get('context'))
if ref:
rule.attrib['context'] = ref
def remove_duplicate_nodes(xml_string):
parser = etree.XMLParser(resolve_entities=False, collect_ids=False)
root = etree.fromstring(xml_string.encode(), parser=parser)
remove_duplicate_list_nodes(root)
remove_duplicate_context_nodes(root)
# reformat comments
xml = etree.tostring(root)
xml = re.sub(b'(?=[^\n ])<!--', b'\n<!--', xml)
xml = re.sub(b'-->(?=[^ \n])', b'-->\n', xml)
# extract DOCTYPE removed by etree.fromstring and reformat <language>
doctype = xml_string[:xml_string.find('<highlighting')]
# remove unformatted <language>
xml = xml[xml.find(b'<highlighting'):]
# last comment removed by etree.fromstring
last_comment = '\n<!-- kate: replace-tabs on; indent-width 2; tab-width 2; -->'
return f'{doctype}{xml.decode()}{last_comment}'
# BEGIN Jinja filters
def cmd_is_nulary(cmd):
return cmd.setdefault('nulary?', False)
# END Jinja filters
@click.command()
@click.argument('input_yaml', type=click.File('r'))
@click.argument('template', type=click.File('r'), default='./cmake.xml.tpl')
def cli(input_yaml, template):
data = yaml.load(input_yaml, Loader=yaml.BaseLoader)
# Partition `variables` and `environment-variables` lists into "pure" (key)words and regexes to match
for var_key in _VAR_KIND_LIST:
data[var_key] = {
k: sorted(set(v)) for k, v in zip(
_KW_RE_LIST
, [*partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data[var_key])]
)
}
data[var_key]['re'] = try_placeholders_to_regex(data[var_key]['re'])
# Transform properties and make all-properties list
data['properties'] = {}
for prop in _PROPERTY_KEYS:
python_prop_list_name = prop.replace('-', '_')
props, props_re = partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data[prop])
del data[prop]
data['properties'][python_prop_list_name] = {
k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [props, props_re])
}
data['properties'][python_prop_list_name]['re'] = try_placeholders_to_regex(props_re)
data['properties']['kinds'] = list(map(lambda name: name.replace('-', '_'), _PROPERTY_KEYS))
# Make all commands list
data['commands'] = list(
map(
transform_command
, data['scripting-commands'] + data['project-commands'] + data['ctest-commands']
)
)
data['standard_module_commands'] = list(
map(
transform_command
, data['standard-module-commands']
)
)
del data['standard-module-commands']
# Fix node names to be accessible from Jinja template
data['generator_expressions'] = (ex for ex in data['generator-expressions'] if isinstance(ex, str))
data['complex_generator_expressions'] = [ex for ex in data['generator-expressions'] if not isinstance(ex, str)]
data['deprecated_or_internal_variables'] = data['deprecated-or-internal-variables']
data['environment_variables'] = data['environment-variables']
del data['generator-expressions']
del data['deprecated-or-internal-variables']
del data['environment-variables']
env = jinja2.Environment(
keep_trailing_newline=True
)
env.block_start_string = '<!--['
env.block_end_string = ']-->'
env.variable_start_string = '<!--{'
env.variable_end_string = '}-->'
env.comment_start_string = '<!--#'
env.comment_end_string = '#-->'
# Register convenience filters
env.tests['nulary'] = cmd_is_nulary
tpl = env.from_string(template.read())
result = tpl.render(data)
result = remove_duplicate_nodes(result)
print(result)
if __name__ == '__main__':
cli()
# TODO Handle execptions and show errors
@@ -0,0 +1,42 @@
#!/usr/bin/env perl
# SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
# SPDX-License-Identifier: MIT
my $file = "";
open(my $input, '<:encoding(UTF-8)', $ARGV[0])
or die "Could not open file '$ARGV[0]': $!";
open(my $output, '>:encoding(UTF-8)', $ARGV[1])
or die "Could not open file '$ARGV[1]': $!";
while (<$input>)
{
$file .= $_;
}
$warning = "\n\n<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT ***** -->\n";
$first_context = " <context attribute=\"Normal Text\" lineEndContext=\"#stay\" name=\"Normal\">
<RegExpr attribute=\"Comment\" context=\"LineComment\" String=\"--(?:!|(?:-(?=[^-]|\$)))\"/>
<RegExpr attribute=\"Region\" context=\"#stay\" String=\"--\\s*\@\\{\\s*\$\" beginRegion=\"MemberGroup\" />
<RegExpr attribute=\"Region\" context=\"#stay\" String=\"--\\s*\@\\}\\s*\$\" endRegion=\"MemberGroup\" />
</context>";
$file =~ s/\n\s*<context [^\n]*?(?:name="ML_|name="BlockComment").*?<\/context>//gs;
$file =~ s/\n\s*<context [^\n]*?name="Normal".*?<\/context>/\n$first_context/s;
$file =~ s/\n[^\n]*?(?: ml_word|LineContinue)[^\n]+//gs;
$file =~ s/\/\/\//---/gs;
$file =~ s/\/\/!/--!/gs;
$language = $file =~ s/.*?(<language[^>]+?>).*/$1/sr;
$language =~ s/ name="[^"]+/ name="DoxygenLua/s;
$language =~ s/ extensions="[^"]+/ extensions="/s;
$language =~ s/ mimetype="[^"]+/ mimetype="/s;
$language =~ s/ priority="[^"]+"//s;
$version = $language =~ s/.*? version="([^"]+).*/$1/sr;
$version = $version+2;
$language =~ s/ version="[^"]+/ version="$version/s;
$file =~ s/<language[^>]+?>/$warning\n$language/s;
print $output $file;
print $output $warning;
@@ -0,0 +1,144 @@
#!/usr/bin/perl
# This perl script read stdin and write on stdout. It shall be an XML language file.
#
# * If the name of the language is 'HTML', then it creates the language 'PHP (HTML)'
# which shall be used for PHP hl.
#
# * If the name of the language is something else (say '*'), it creates the language '*/PHP'.
# This new language is the same as the old one, but is able to detect PHP everywhere.
#
# This script will correctly set extensions & mimetype, and will replace
# <IncludeRules context="##*"> by <IncludeRules context="##*/PHP">
#
# Generated languages need a language named 'PHP/PHP', which shall take care of PHP hl itself
# and which will be called every time something like <?php is encountred.
#
# This script also supports Twig and does the same as for PHP.
#
# SPDX-FileCopyrightText: Jan Villat <jan.villat@net2000.ch>
# License: LGPL
my $file = "";
open(my $input, '<:encoding(UTF-8)', $ARGV[0])
or die "Could not open file '$ARGV[0]': $!";
open(my $output, '>:encoding(UTF-8)', $ARGV[1])
or die "Could not open file '$ARGV[1]': $!";
my $language = $ARGV[1];
if ($language =~ /-php\.xml$/)
{
$language = "PHP";
}
else
{
$language = "Twig";
}
while (<$input>)
{
$file .= $_;
}
$warning = "\n\n<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT ***** -->\n";
$file =~ s/(?=<language)/$warning\n\n\n/;
$file =~ /<language.*?name="([^"]+)"/;
my $syntaxName = $1;
if ($syntaxName eq "HTML")
{
$root = 1;
}
if ($language eq "Twig")
{
$file =~ s/<language([^>]+)priority="[^"]*"/<language$1/s;
}
if ($root == 1)
{
$file =~ s/<language([^>]+)name="[^"]*"/<language$1name="$language (HTML)"/s;
$file =~ s/<language([^>]+)section="[^"]*"/<language$1section="Scripts"/s;
if ($language eq "PHP")
{
$file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="*.php;*.php3;*.wml;*.phtml;*.phtm;*.inc;*.ctp"/s;
$file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="text\/x-php4-src;text\/x-php3-src;text\/vnd.wap.wml;application\/x-php"/s;
$file =~ s/<language([^>]+)*/<language$1 indenter="cstyle"/s;
}
# Twig
else
{
$file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="*.twig;*.html.twig;*.htm.twig"/s;
$file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="text\/x-twig"/s;
}
}
else
{
$file =~ s/<language([^>]+)hidden="[^"]*"/<language$1/s;
$file =~ s/<language([^>]+)section="[^"]*"/<language$1section="Other"/s;
my $extra = " hidden=\"true\"";
my $mimetype = "";
my $extensions = "";
if ($language eq "Twig")
{
$mimetype = "text/x-twig";
if ($syntaxName eq "JavaScript")
{
$extra = " priority=\"1\"";
$extensions = "*.js.twig;*.mjs.twig;*.cjs.twig";
}
elsif ($syntaxName eq "TypeScript")
{
$extra = " priority=\"1\"";
$extensions = "*.ts.twig;*.mts.twig;*.cts.twig";
}
}
$file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="$mimetype"/s;
$file =~ s/<language([^>]+)name="([^"]*)"/<language$1name="$2\/$language"$extra/s;
$file =~ s/<language([^>]+)alternativeNames="([^"]*)"/<language$1alternativeNames="$2\/$language"/s;
$file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="$extensions"/s;
}
# replace list with a include
$file =~ s/<list name="([^"]+)">.*?<\/list>/<list name="$1"><include>$1##$syntaxName<\/include><\/list>/gs;
$file =~ s/<language([^>]+)kateversion="[^"]*"/<language$1kateversion="5.79"/s;
$file =~ s/ fallthrough="(true|1)"//gs;
if ($language eq "Twig")
{
# remove Mustache syntax
if ($root == 1)
{
$file =~ s/<context name="MustacheJS.*?<\/context>//gs;
$file =~ s/<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="[^"]+[^\/]+\/>//gs;
}
}
elsif ($root == 1 || $ARGV[0] =~ /mustache.xml$/)
{
$file =~ s/<(?:RegExpr (attribute="Processing Instruction" context="PI"|context="PI" attribute="Processing Instruction")|itemData name="Processing Instruction")[^\/]+\/>|<context name="PI".*?<\/context>//gs;
}
my $find_language = "##$language/$language";
my $language_suffix = "/$language";
if ($language eq "PHP")
{
$find_language = "FindPHP";
}
$file =~ s/<IncludeRules\s([^>]*)context="([^"#]*)##(?!Alerts|Comments|Doxygen|Modelines)([^"]+)"/<IncludeRules $1context="$2##$3$language_suffix"/g;
$file =~ s/(<context\s[^>]*[^>\/]>)/$1\n<IncludeRules context="$find_language" \/>/g;
$file =~ s/(<context\s[^>]*[^>\/])\s*\/>/$1>\n<IncludeRules context="$find_language" \/>\n<\/context>/g;
if ($language eq "PHP")
{
$findphp = "<context name=\"FindPHP\" attribute=\"Normal Text\" lineEndContext=\"#stay\">\n<Detect2Chars context=\"##PHP/PHP\" char=\"&lt;\" char1=\"?\" lookAhead=\"true\" />\n</context>\n";
$file =~ s/(?=<\/contexts\s*>)/$findphp/;
}
print $output $file;
print $output $warning;
@@ -0,0 +1,55 @@
#!/usr/bin/env ruby
#
# Generates the keyword lists for directives and variables used in nginx and
# prints them to stdout, ready to be copy & pasted into nginx.xml.
#
# SPDX-FileCopyrightText: 2023 Jyrki Gadinger <nilsding@nilsding.org>
# SPDX-License-Identifier: MIT
#
# Usage:
# % ./generate-nginx-lists.rb
#
# if you want to install the required dependencies provide `INSTALL_GEMS` in
# your ENV:
# % INSTALL_GEMS=1 ./generate-nginx-lists.rb
require "bundler/inline"
gemfile(ENV["INSTALL_GEMS"]) do
source "https://rubygems.org"
gem "nokogiri", "~> 1.14"
gem "faraday", "~> 2.7"
gem "builder", "~> 3.2"
end
def fetch_vars(url)
Faraday.get(url)
.then { Nokogiri::HTML.parse _1.body }
.css("div#content a[href]")
.map(&:text)
.reject { _1.end_with?("_") } # some vars are just a prefix, ignore those
.sort
.uniq
end
def build_xml_list(name, url: nil, items: [])
builder = Builder::XmlMarkup.new(indent: 2)
builder.comment! "see #{url} for a full list of #{name}"
builder.list(name:) do |b|
items.each do |item|
b.item item
end
end
end
{
directives: "https://nginx.org/en/docs/dirindex.html",
variables: "https://nginx.org/en/docs/varindex.html",
}.each do |name, url|
items = fetch_vars(url)
puts build_xml_list(name, url:, items:).gsub(/^/, ' ' * 4)
puts
end
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Generate SPDX-Comments syntax file
#
# SPDX-FileCopyrightText: 2020 Alex Turbov <i.zaufi@gmail.com>
# SPDX-License-Identifier: MIT
#
# To install prerequisites:
#
# $ pip install --user click jinja2
#
# To use:
#
# $ ./generate-spdx-syntax.py > ../syntax/spdx-comments.xml
#
import json
import pathlib
import urllib.request
import click
import jinja2
def get_json(url):
with urllib.request.urlopen(url=url) as body:
return json.load(body)
@click.command()
@click.argument('template', type=click.File('r'), default='./spdx-comments.xml.tpl')
def cli(template):
data = {
'licenses': [
*filter(
lambda l: not l['licenseId'].endswith('+')
, get_json(url='https://spdx.org/licenses/licenses.json')['licenses']
)
]
, 'exceptions': [
*filter(
lambda l: not l['licenseExceptionId'].endswith('+')
, get_json(url='https://spdx.org/licenses/exceptions.json')['exceptions']
)
]
}
env = jinja2.Environment(
keep_trailing_newline=True
)
env.block_start_string = '<!--['
env.block_end_string = ']-->'
env.variable_start_string = '<!--{'
env.variable_end_string = '}-->'
env.comment_start_string = '<!--#'
env.comment_end_string = '#-->'
tpl = env.from_string(template.read())
result = tpl.render(data)
print(result.strip(), end=None)
if __name__ == '__main__':
cli()
# TODO Handle execptions and show errors
@@ -0,0 +1,40 @@
#!/bin/bash
#
# SPDX-FileCopyrightText: 2012-2013 Alex Turbov
#
# Grab a documented (officially) class list from Qt project web site:
# http://qt-project.org/doc/qt-${version}/classes.html
#
version=$1
shift
case "$version" in
5*)
url="http://qt-project.org/doc/qt-${version}/qtdoc/classes.html"
;;
4*)
url="http://qt-project.org/doc/qt-${version}/classes.html"
;;
*)
echo "*** Error: Only Qt4 and Qt5 supported!"
esac
if [ -n "$version" ]; then
tmp=`mktemp`
wget -O $tmp "$url"
cat $tmp | egrep '^<dd><a href=".*\.html">.*</a></dd>$' \
| sed -e 's,<dd><a href=".*\.html">\(.*\)</a></dd>,<item> \1 </item>,' \
| grep -v 'qoutputrange'
rm $tmp
else
cat <<EOF
Usage:
$0 Qt-version
Note: Only major and minor version required
Example:
$0 4.8
EOF
fi
@@ -0,0 +1,33 @@
#!/bin/bash
#
# SPDX-FileCopyrightText: 2011-2012 Alex Turbov
#
# Simplest (and stupid) way to get #defines from a header file(s)
#
# TODO Think about to use clang to get (an actual) list of free functions and/or types, classes, etc
# Using python bindings it seems possible and not so hard to code...
#
basepath=$1
shift
if [ -n "$*" ]; then
for f in $*; do
egrep '^\s*#\s*define\s+(Q|QT|QT3)_' ${basepath}/$f
done \
| sed 's,^\s*#\s*define\s\+\(Q[A-Z0-9_]\+\).*,<item> \1 </item>,' \
| sort \
| uniq \
| grep -v EXPORT \
| grep -v QT_BEGIN_ \
| grep -v QT_END_ \
| grep -v QT_MANGLE_
else
cat <<EOF
Usage:
$0 basepath [qt-header-filenames]
Example:
$0 /usr/include/qt4/Qt qglobal.h qconfig.h qfeatures.h
EOF
fi
@@ -0,0 +1,23 @@
#!/usr/bin/env python
from __future__ import print_function
tokens = []
with open("makensiscmdhelp.output") as f: # output from `makensis /cmdhelp`
for line in f:
if line.startswith(" "):
continue # line continuation
tokens.append(line.split()[0])
keywords = [x[1:] for x in tokens if x.startswith("!")]
basefuncs = [x for x in tokens if not x.startswith("!")]
print("KEYWORDS")
for keyword in keywords:
print("<item> %s </item>" % keyword)
print()
print("BASEFUNCS")
for basefunc in basefuncs:
print("<item> %s </item>" % basefunc)
@@ -0,0 +1,46 @@
#!/usr/bin/env python
# SPDX-FileCopyrightText: 2016 Kevin Funk <kfunk@kde.org>
#
# SPDX-License-Identifier: LGPL-2.0-or-later
# This script will print XML-like code you can put into the qmake.xml
# syntax definition file
#
# Prerequisite: You need to have a qtbase checkout somewhere
#
# Usage: qmake-gen.py /path/to/qtbase/
from __future__ import print_function
import subprocess
import os
import sys
qt5Source = sys.argv[1]
qmakeKeywords = subprocess.check_output("ag --nofilename 'QMAKE_[A-Z_0-9]+' {0}/mkspecs -o".format(qt5Source), shell=True).split(os.linesep)
extraKeywords = subprocess.check_output("sed -nr 's/\\\section1 ([A-Z_0-9]{{2,100}}).*/\\1/p' {0}/qmake/doc/src/qmake-manual.qdoc".format(qt5Source), shell=True).split(os.linesep)
keywords = []
keywords = [x.strip() for x in qmakeKeywords]
keywords += [x.strip() for x in extraKeywords]
keywords = list(set(keywords)) # remove duplicates
keywords.sort()
functions = subprocess.check_output("sed -nr 's/\{{ \\\"(.+)\\\", T_.+/\\1/p' {0}/qmake/library/qmakebuiltins.cpp".format(qt5Source), shell=True).split(os.linesep)
functions.sort()
def printItems(container):
for item in container:
trimmedText = item.strip()
if not trimmedText:
continue
print("<item> %s </item>" % trimmedText)
print()
print("KEYWORDS")
printItems(keywords)
print("FUNCTIONS")
printItems(functions)
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language>
<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT *****
cd data/generators
# increase version of spdx-comments.xml.tpl then
./generate-spdx-syntax.py > ../syntax/spdx-comments.xml
-->
<language
version="6"
kateversion="3.1"
name="SPDX-Comments"
section="Other"
extensions=""
mimetype=""
author="Alex Turbov (i.zaufi@gmail.com)"
license="MIT"
hidden="true"
>
<highlighting>
<list name="tags">
<item>SPDX-License-Identifier:</item>
<item>SPDX-FileContributor:</item>
<item>SPDX-FileCopyrightText:</item>
<item>SPDX-LicenseInfoInFile:</item>
</list>
<list name="operators">
<item>AND</item>
<item>OR</item>
<item>WITH</item>
</list>
<list name="licenses">
<!--[- for license in licenses if not license.isDeprecatedLicenseId ]-->
<item><!--{ license.licenseId }--></item>
<!--[- endfor ]-->
</list>
<list name="deprecated-licenses">
<!--[- for license in licenses if license.isDeprecatedLicenseId ]-->
<item><!--{ license.licenseId }--></item>
<!--[- endfor ]-->
</list>
<list name="exceptions">
<!--[- for exception in exceptions if not exception.isDeprecatedLicenseId ]-->
<item><!--{ exception.licenseExceptionId }--></item>
<!--[- endfor ]-->
</list>
<list name="deprecated-exceptions">
<!--[- for exception in exceptions if exception.isDeprecatedLicenseId ]-->
<item><!--{ exception.licenseExceptionId }--></item>
<!--[- endfor ]-->
</list>
<contexts>
<context name="Normal" attribute="SPDX Tag" lineEndContext="#pop">
<WordDetect String="SPDX-License-Identifier:" attribute="SPDX Tag" context="license-expression" />
<keyword String="tags" attribute="SPDX Tag" />
</context>
<context name="license-expression" attribute="SPDX Value" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<DetectSpaces/>
<AnyChar String="()+" context="#stay" attribute="SPDX License Expression Operator" />
<keyword String="licenses" context="#stay" attribute="SPDX License" />
<keyword String="deprecated-licenses" context="#stay" attribute="SPDX Deprecated License" />
<keyword String="exceptions" context="#stay" attribute="SPDX License Exception" />
<keyword String="deprecated-exceptions" context="#stay" attribute="SPDX Deprecated License Exception" />
<keyword String="operators" context="#stay" attribute="SPDX License Expression Operator" />
<RegExpr attribute="SPDX License" context="#stay" String="\bLicenseRef-[^\s]+" />
</context>
</contexts>
<itemDatas>
<itemData name="SPDX Tag" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
<itemData name="SPDX Value" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
<itemData name="SPDX License" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
<itemData name="SPDX License Exception" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
<itemData name="SPDX Deprecated License" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
<itemData name="SPDX Deprecated License Exception" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
<itemData name="SPDX License Expression Operator" defStyleNum="dsOperator" italic="true" spellChecking="false" />
</itemDatas>
</highlighting>
<general>
<keywords casesensitive="1" weakDeliminator=":-." />
</general>
</language>
<!-- kate: indent-width 2; -->
@@ -0,0 +1,498 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Jonathan Poelen <jonathan.poelen@gmail.com>
# SPDX-License-Identifier: MIT
from pathlib import Path
from collections import defaultdict
from typing import TextIO
import re
import sys
exclude_line = {
' - non-standard\n',
' - experimental\n',
' - deprecated\n',
'page-type: css-combinator\n',
'page-type: css-selector\n',
'page-type: css-module\n',
'page-type: landing-page\n',
'page-type: guide\n',
}
page_type_accepted = {
'page-type: css-type\n',
'page-type: css-function\n',
'page-type: css-property\n',
'page-type: css-keyword\n',
'page-type: css-shorthand-property\n',
'page-type: css-pseudo-element\n',
'page-type: css-pseudo-class\n',
'page-type: css-at-rule-descriptor\n',
'page-type: css-at-rule\n',
'page-type: css-media-feature\n',
'page-type: svg-attribute\n',
}
exclude_title = {
'<alpha-value>',
'<angle>',
'<angle-percentage>',
'<basic-shape>',
'<calc-constant>',
'<calc-sum>',
'<color-interpolation-method>',
'<color>',
'<custom-ident>',
'<dashed-ident>',
'<display-listitem>',
'<display-inside>',
'<dimension>',
'<easing-function>'
'<filter-function>',
'<flex>',
'<frequency-percentage>',
'<frequency>',
'<gradient>',
'<hex-color>',
'<hue>',
'<hue-interpolation-method>',
'<ident>',
'<image>',
'<integer>',
'<length>',
'<length-percentage>',
'<number>',
'<percentage>',
'<position>',
'<ratio>',
'<resolution>',
'<string>',
'<time-percentage>',
'<time>',
'<transform-function>',
'"!important"',
}
properties_ignore_value = (
'counter-increment',
'counter-reset',
'counter-set',
'text-rendering',
'page',
)
units: list[str] = []
colors: set[str] = set()
system_colors: set[str] = set()
deprecated_system_colors: set[str] = set()
values: set[str] = set()
properties: set[str] = set()
svg_values: set[str] = set()
svg_properties: set[str] = set()
functions: set[str] = set()
pseudo_classes: set[str] = set()
pseudo_elements: set[str] = set()
experimental_pseudo_classes: set[str] = set()
experimental_pseudo_elements: set[str] = set()
at_rules: set[str] = set()
media_features: set[str] = set()
media_feature_values: set[str] = set()
_update_version_extractor = re.compile(r' version="(\d+)" ')
def update_version(s: str) -> str:
return _update_version_extractor.sub(lambda m: f' version="{int(m[1])+1}" ', s, count=1)
_md_value_extractor = re.compile(r'(?<=[^\w][ /])`([-\w][-\w\d]+(?:<[^>]+>[?+*])?)`')
_html_value_extractor = re.compile(r'<code>([-\w][-\w\d]+)</code>')
_is_md_value = re.compile(r'^\s*- `')
_is_html_table_desc = re.compile(r'^\s+<td><code>')
def css_parse_values(f: TextIO, prop: str, values: set[str]) -> None:
line:str = ''
# Format:
# ## Syntax or ### Syntax
#
# ```css
# (optional)
# ```
# ## Values or ### Values or not...
#
# - `ident` or html table <td><code>....</code></td>
#
# ## SVG only ... (optional)
# ## other title
for line in f:
if line.endswith('## Syntax\n') or line.endswith('## Values\n') or '## SVG only' in line:
for line in f:
if _is_md_value.match(line):
if 'deprecated' not in line:
values.update(_md_value_extractor.findall(line))
elif line.startswith('#'):
if not (line.endswith('## Values\n') or '## SVG only' in line
or (prop == 'display'
and (line.endswith('## Grouped values\n')
or line.endswith('## Outside\n')
or line.endswith('## Inside\n')
or line.endswith('## List Item\n')
or line.endswith('## Internal\n')
or line.endswith('## Box\n')
or line.endswith('## Precomposed\n')
))
):
return
elif line == '```css\n':
for line in f:
if line.startswith('```\n'):
break
elif _is_html_table_desc.match(line):
values.update(_html_value_extractor.findall(line))
def css_parse_named_colors(f: TextIO) -> set[str]:
return set(re.findall('\n <td>(?:\n )?<code>([a-z]+)</code>', f.read()))
def css_parse_units(f: TextIO) -> list[str]:
return re.findall(r'`([^`]+)`', ''.join(re.findall(r'\n\| (`[^|]+)', f.read())))
_svg_values_extractor = re.compile(r'<th scope="row">Value</th>\n\s*<td>(.*?)</td>', re.DOTALL)
_svg_value_extractor = re.compile(r'<code>([-\w\d]+)</code>')
def css_parse_svg_attribute(f: TextIO, prop: str, properties: set[str], values: set[str]) -> None:
contents = f.read()
if 'can be used as a CSS property' in contents:
properties.add(prop)
m = _svg_values_extractor.search(contents)
if m:
values.update(_svg_value_extractor.findall(m[1]))
_experimental_selector_extractor = re.compile(r'\n- {{CSSxRef([^}]+)}} {{Experimental_Inline}}')
_selector_extractor = re.compile(r'":+([-\w\d]+)[()]*"')
def css_parse_pseudo_classes_or_elements(f: TextIO) -> tuple[
set[str], # experimental
list[str]
]:
s = f.read()
experimental_str = ''.join(_experimental_selector_extractor.findall(s))
return (set(_selector_extractor.findall(experimental_str)), _selector_extractor.findall(s))
if len(sys.argv) < 5:
print(f'''{Path(sys.argv[0]).name} content-main-directory syntax/css.xml sass-site-directory syntax/scss.xml
content-main-directory is https://github.com/mdn/content/ (https://github.com/mdn/content/archive/refs/heads/main.zip)
sass-site-directory is https://github.com/sass/sass-site/tree/main (https://github.com/sass/sass-site/archive/refs/heads/main.zip)
''', file=sys.stderr)
exit(1)
css_dir = Path(sys.argv[1])
css_filename = Path(sys.argv[2])
scss_dir = Path(sys.argv[3])
scss_filename = Path(sys.argv[4])
tmp_pseudo_classes = (set(), ())
tmp_pseudo_elements = (set(), ())
for pattern in (
'files/en-us/web/svg/attribute/**/',
'files/en-us/web/css/**/',
):
for md in css_dir.glob(pattern):
with open(md/'index.md', encoding='utf8') as f:
if f.readline() != '---\n':
continue
title = f.readline()[7:-1]
if title in exclude_title:
continue
if title.startswith('"'):
title = title[1:-1]
page_type = ''
for line in f:
if line in exclude_line:
page_type = ''
break
if line.startswith('page-type: '):
if line not in page_type_accepted:
raise Exception(f'Unknown {line[:-1]}')
page_type = line[11:-1]
if line == '---\n':
break
if page_type == 'css-property' or page_type == 'css-at-rule-descriptor':
properties.add(title)
if not title.endswith('-name') and title not in properties_ignore_value:
css_parse_values(f, title, values)
elif page_type == 'css-shorthand-property':
properties.add(title)
elif page_type == 'css-pseudo-class':
pseudo_classes.add(title[1:].removesuffix('()'))
elif page_type == 'css-pseudo-element':
pseudo_elements.add(title[2:].removesuffix('()'))
elif page_type == 'css-type':
if title == '<named-color>':
colors = css_parse_named_colors(f)
if title == '<system-color>':
css_parse_values(f, '', system_colors)
deprecated_system_colors = set(re.findall('\n- `([^`]+)` {{deprecated_inline}}', f.read()))
else:
css_parse_values(f, '', values)
elif page_type == 'css-function':
functions.add(title[:-2])
elif page_type == 'css-at-rule':
at_rules.add(title)
elif page_type == 'css-media-feature':
media_features.add(title)
css_parse_values(f, title, media_feature_values)
elif page_type == 'css-keyword':
values.add(title)
elif title == 'CSS values and units':
units = css_parse_units(f)
elif title == 'Pseudo-classes':
tmp_pseudo_classes = css_parse_pseudo_classes_or_elements(f)
elif title == 'Pseudo-elements':
tmp_pseudo_elements = css_parse_pseudo_classes_or_elements(f)
elif page_type == 'svg-attribute':
css_parse_svg_attribute(f, title, svg_properties, svg_values)
elif title == 'CSS value functions':
functions.update(re.findall(r'\n- {{CSSxRef\("[^"]+", "([-\w\d]+)\(\)"\)}}\n', f.read()))
experimental_pseudo_classes = tmp_pseudo_classes[0]
experimental_pseudo_classes -= pseudo_classes
pseudo_classes.update(tmp_pseudo_classes[1])
experimental_pseudo_elements = tmp_pseudo_elements[0]
experimental_pseudo_elements -= pseudo_elements
pseudo_elements.update(tmp_pseudo_elements[1])
global_values = {
'auto',
'inherit',
'initial',
'revert',
'revert-layer',
'unset',
}
values -= global_values
svg_values -= global_values
pseudo_classes -= experimental_pseudo_classes
pseudo_elements -= experimental_pseudo_elements
# add values of functions
values.update((
# repeat()
'auto-fill',
'auto-fit',
))
# move some keyword colors in values
for special_color in ('transparent', 'currentcolor'):
values.add(special_color)
colors.discard(special_color)
# fix not specified value in mdn file
if 'user-invalid' in experimental_pseudo_classes:
pseudo_classes.discard('user-valid')
experimental_pseudo_classes.add('user-valid')
media_features.update((
'min-width',
'max-width',
'min-height',
'max-height',
))
# fix errors in mdn file
for e in ('has', 'host-context'):
pseudo_classes.add(e)
experimental_pseudo_classes.discard(e)
# @font-format functions
functions.update((
'format',
'local',
'tech',
))
# def show(name, values):
# print(f'{name} ({len(values)}):')
# print('\n'.join(sorted(values)), end='\n\n')
#
# show('properties', properties)
# show('svg properties', svg_properties)
# show('values', values)
# show('svg values', svg_values)
# show('global values', global_values)
# show('functions', functions)
# show('pseudo-classes', pseudo_classes)
# show('pseudo-elements', pseudo_elements)
# show('experimental pseudo-classes', experimental_pseudo_classes)
# show('experimental pseudo-elements', experimental_pseudo_elements)
# show('at-rules', at_rules)
# show('media-features', media_features)
# show('media-features values', media_feature_values)
# show('colors', colors)
# show('system colors', system_colors)
# show('deprecated system colors', deprecated_system_colors)
# show('units', units)
# print('units reg:', '|'.join(units))
#
# Update CSS
#
sep = '\n '
css_replacements = {
prop: f'</item>{sep}<item>'.join(sorted(seq))
for prop, seq in (
('properties', properties),
('values', values),
('value keywords', global_values),
('functions', functions),
('pseudo-classes', pseudo_classes),
('pseudo-elements', pseudo_elements),
('media features', media_features)
)
}
for prop, seq in (('properties', svg_properties - properties), ('values', svg_values - values)):
if seq:
items = f'</item>{sep}<item>'.join(sorted(seq))
css_replacements[prop] += f'</item>\n{sep}<!-- SVG only -->\n{sep}<item>{items}'
rep1 = f'</item>{sep}<item>'.join(sorted(colors))
rep2 = f'</item>{sep}<item>'.join(sorted(system_colors))
css_replacements['colors'] = f'{rep1}</item>{sep}{sep}<!-- System colors -->{sep}<item>{rep2}'
item_extractor = re.compile('<item>([^-<][^<]*)')
current_at_rules = set()
def _css_update_and_extract_items(m) -> str:
seq = css_replacements.get(m[1])
if seq:
end = ' ' if m[3] == '</list>' else sep
return f'<list name="{m[1]}">{sep}<item>{seq}</item>\n{end}{m[3]}'
current_at_rules.update(item_extractor.findall(m[2]))
return m[0]
css_content = css_filename.read_text()
original_css_content = css_content
names = f"{'|'.join(css_replacements)}|at-rules(?: definitions)?"
css_content = re.sub(rf'<list name="({names})">(.*?)(</list>|<!-- manual list -->)',
_css_update_and_extract_items, css_content, flags=re.DOTALL)
_regexpr_unit_prefix = r'(<RegExpr attribute="Unit".*?String="\(%\|\()'
regexpr_unit_extractor = re.compile(fr'{_regexpr_unit_prefix}([^)]+)')
css_content = regexpr_unit_extractor.sub('\\1' + "|".join(units), css_content, 1)
if original_css_content != css_content:
css_content = update_version(css_content)
css_filename.write_text(css_content)
def show_at_rule_difference(language: str, old_at_rules: set[str], new_at_rules: set[str]) -> None:
at_rule_added = new_at_rules - old_at_rules
at_rule_removed = old_at_rules - new_at_rules
nl = '\n '
if at_rule_added or at_rule_removed:
print(f"""\x1b[31m{language} At-rules requires a manual update
New ({len(at_rule_added)}):\x1b[0m
{nl.join(at_rule_added)}
\x1b[31mRemoved ({len(at_rule_removed)}):\x1b[0m
{nl.join(at_rule_removed)}""")
show_at_rule_difference('CSS', current_at_rules, at_rules)
#
# Extract SCSS data
#
scss_functions:list[str] = []
scss_at_rules:set[str] = {'@content', '@return'}
_function_list_extractor = re.compile(r'{% function (.*?) %}')
_function_extractor = re.compile(r"'([-._a-zA-Z0-9]+)\(")
_at_rule_extractor = re.compile(r'@[-a-z0-9]+')
for md in sorted(scss_dir.glob('source/documentation/modules/**/*.md')):
func_list = _function_list_extractor.findall(md.read_text())
func_items = set(_function_extractor.findall(''.join(func_list)))
scss_functions.append(f'\n{sep}<!-- {md.stem} -->')
scss_functions.extend(f'{sep}<item>{func}</item>' for func in sorted(func_items - functions))
for md in scss_dir.glob('source/documentation/at-rules/**/*.md'):
with open(md) as f:
f.readline()
scss_at_rules.update(_at_rule_extractor.findall(f.readline()))
subproperties = set(
'-'.join(splitted[i:n])
for prop in properties
for splitted in (prop.rsplit('-', prop.count('-') - 1) # '-aaa-bbb' -> ['-aaa', 'bbb']
if prop.startswith('-')
else prop.split('-'), ) # 'aaa-bbb' -> ['aaa', 'bbb']
for i in range(len(splitted))
for n in range(i+1, len(splitted)+1)
)
#
# Update SCSS
#
scss_current_at_rules = set()
def _scss_update_and_extract_items(m) -> str:
name = m[1]
if name == 'functions':
return f"""<list name="functions">
<include>functions##CSS</include>
<!-- https://sass-lang.com/documentation/modules/ -->{f''.join(scss_functions)}
</list>"""
if name == 'at-rules':
scss_current_at_rules.update(_at_rule_extractor.findall(m[2]))
return m[0]
# sub-properties
items = f'</item>{sep}<item>'.join(sorted(subproperties - properties))
return f'<list name="{name}">{sep}<item>{items}</item>\n </list>'
scss_content = scss_filename.read_text()
original_scss_content = scss_content
scss_content = re.sub(r'<list name="(sub-properties|functions|at-rules)">(.*?)</list>',
_scss_update_and_extract_items, scss_content, count=3, flags=re.DOTALL)
scss_content = re.sub(r'<!ENTITY pseudoclasses "[^"]*">',
f'<!ENTITY pseudoclasses "{"|".join(sorted(pseudo_classes))}">',
scss_content, count=1)
scss_content = regexpr_unit_extractor.sub('\\1' + "|".join(units), scss_content, 1)
if original_scss_content != scss_content:
scss_content = update_version(scss_content)
scss_filename.write_text(scss_content)
show_at_rule_difference('SCSS', scss_current_at_rules, scss_at_rules)
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2023 Jonathan Poelen <jonathan.poelen@gmail.com>
# SPDX-License-Identifier: MIT
from pathlib import Path
from urllib import request
import re
import sys
if len(sys.argv) < 1:
print(f'{sys.argv[0]} syntax/less.xml', file=sys.stderr)
exit(1)
#
# Extract functions
#
data = request.urlopen('https://lesscss.org/functions/').read().decode()
functions = re.findall(r'</a>([-_\w\d]+)</h3>', data, flags=re.DOTALL)
functions.append('%')
#
# Update syntax
#
sep = '\n '
new_list = f"""<list name="functions">
<include>functions##CSS</include>
<!-- Less functions, @see http://lesscss.org/functions/ -->
<item>{f'</item>{sep}<item>'.join(sorted(functions))}</item>
</list>"""
less_filename = Path(sys.argv[1])
less_content = less_filename.read_text()
original_less_content = less_content
less_content = re.sub(r'<list name="functions">.*?</list>',
new_list, less_content, count=1, flags=re.DOTALL)
if original_less_content != less_content:
less_content = re.sub(' version="(\d+)" ', lambda m: f' version="{int(m[1])+1}" ',
less_content, count=1)
less_filename.write_text(less_content)