From 7eb26e73ea1f2a3de976841bec3add0bafd9c2b5 Mon Sep 17 00:00:00 2001 From: quinox Date: Sat, 4 May 2024 10:24:28 +0200 Subject: [PATCH] Generate vim syntax based on the documentation --- man/Makefile | 5 +- man/flashmq.conf.5.dbk5 | 22 +++--- man/flashmq.vim | 133 ++++++++++++++++++++++++++++++++++++ man/vim/syntax.generate.sh | 60 ++++++++++++++++ man/vim/syntax.template.vim | 42 ++++++++++++ 5 files changed, 250 insertions(+), 12 deletions(-) create mode 100644 man/flashmq.vim create mode 100755 man/vim/syntax.generate.sh create mode 100644 man/vim/syntax.template.vim diff --git a/man/Makefile b/man/Makefile index e3c6bd43..a48bec9f 100644 --- a/man/Makefile +++ b/man/Makefile @@ -1,4 +1,4 @@ -TARGETS := flashmq.1 flashmq.conf.5 flashmq.1.html flashmq.conf.5.html +TARGETS := flashmq.1 flashmq.conf.5 flashmq.1.html flashmq.conf.5.html flashmq.vim all: $(TARGETS) @@ -29,3 +29,6 @@ clean: %.html: docbook5-to-html5/docbook5-to-html5.xsl %.dbk5 xsltproc $^ > $@ + +%.vim: + vim/syntax.generate.sh vim/syntax.template.vim flashmq.conf.5.dbk5 > $@ diff --git a/man/flashmq.conf.5.dbk5 b/man/flashmq.conf.5.dbk5 index b1249f7d..6361402c 100644 --- a/man/flashmq.conf.5.dbk5 +++ b/man/flashmq.conf.5.dbk5 @@ -575,7 +575,7 @@ Listen parameters can only be used within listen { } blocks. - + @@ -605,7 +605,7 @@ - + @@ -621,7 +621,7 @@ - + @@ -643,7 +643,7 @@ - + inet4address @@ -651,7 +651,7 @@ - + inet6address @@ -659,7 +659,7 @@ - + /foobar/server.crt @@ -667,7 +667,7 @@ - + /foobar/server.key @@ -676,7 +676,7 @@ - + /foobar/client_authority.crt @@ -688,7 +688,7 @@ - + /foobar/dir_with_certificates @@ -703,7 +703,7 @@ - + true/false @@ -721,7 +721,7 @@ - + true/false diff --git a/man/flashmq.vim b/man/flashmq.vim new file mode 100644 index 00000000..0e74755f --- /dev/null +++ b/man/flashmq.vim @@ -0,0 +1,133 @@ +" Vim syntax file +" Language: FlashMQ configuration file + +" +" Goals: +" +" Make incorrect / unknown options stand out. Special attention is given to +" also have this work correctly for blocks, E.G., the toplevel directive +" `log_file` is colored in a global scope but not when it's used inside a +" `listen` block +" +" https://vimdoc.sourceforge.net/htmldoc/syntax.html +" +" TODO: +" - Test number of arguments, specifically: most options take 1 argument, but +" fe. bridge__subscribe takes an optional second argument +" - Deal with quoted options? +" + +if exists("b:current_syntax") + finish +endif +let b:current_syntax = "flashmq" + +syn region fmqComment display oneline start='^\s*#' end='$' + +" We "render" fmqWrongBlock as Error (which makes it red), but then also use +" transparent which makes it be the color of the parent, so it all becomes a +" neutral color. +" Without the transparent+Error trick you would see fmqTopLevelDirective +" highlighted inside blocks which is undesirable: you can't specify `log_file` +" inside a `listen` block, so it shouldn't get colorized. +" +" Real blocks (like `listen` and `bridge`) are defined later and thus get a +" higher priority, and will replace this match. +syn region fmqWrongBlock start=+^.*{+ end=+}+ transparent contains=NONE,fmqComment + +hi link fmqComment Comment +hi link fmqWrongBlock Error +hi link fmqTopLevelDirective Type + +" The rest of this file has been dynamically generated +" +" Local scopes +" + +syn match fmqTopLevelDirective "^\s*allow_anonymous\s" +syn match fmqTopLevelDirective "^\s*allow_unsafe_clientid_chars\s" +syn match fmqTopLevelDirective "^\s*allow_unsafe_username_chars\s" +syn match fmq_bridge_Directive "^\s*address\s" contained +syn match fmq_bridge_Directive "^\s*bridge_protocol_bit\s" contained +syn match fmq_bridge_Directive "^\s*ca_dir\s" contained +syn match fmq_bridge_Directive "^\s*ca_file\s" contained +syn match fmq_bridge_Directive "^\s*clientid_prefix\s" contained +syn match fmq_bridge_Directive "^\s*inet_protocol\s" contained +syn match fmq_bridge_Directive "^\s*keepalive\s" contained +syn match fmq_bridge_Directive "^\s*local_clean_start\s" contained +syn match fmq_bridge_Directive "^\s*local_session_expiry_interval\s" contained +syn match fmq_bridge_Directive "^\s*local_username\s" contained +syn match fmq_bridge_Directive "^\s*max_incoming_topic_aliases\s" contained +syn match fmq_bridge_Directive "^\s*max_outgoing_topic_aliases\s" contained +syn match fmq_bridge_Directive "^\s*port\s" contained +syn match fmq_bridge_Directive "^\s*protocol_version\s" contained +syn match fmq_bridge_Directive "^\s*publish\s" contained +syn match fmq_bridge_Directive "^\s*remote_clean_start\s" contained +syn match fmq_bridge_Directive "^\s*remote_password\s" contained +syn match fmq_bridge_Directive "^\s*remote_retain_available\s" contained +syn match fmq_bridge_Directive "^\s*remote_session_expiry_interval\s" contained +syn match fmq_bridge_Directive "^\s*remote_username\s" contained +syn match fmq_bridge_Directive "^\s*subscribe\s" contained +syn match fmq_bridge_Directive "^\s*tls\s" contained +syn match fmq_bridge_Directive "^\s*use_saved_clientid\s" contained +syn match fmqTopLevelDirective "^\s*client_initial_buffer_size\s" +syn match fmqTopLevelDirective "^\s*client_max_write_buffer_size\s" +syn match fmqTopLevelDirective "^\s*expire_retained_messages_after_seconds\s" +syn match fmqTopLevelDirective "^\s*expire_sessions_after_seconds\s" +syn match fmqTopLevelDirective "^\s*include_dir\s" +syn match fmq_listen_Directive "^\s*allow_anonymous\s" contained +syn match fmq_listen_Directive "^\s*client_verification_ca_dir\s" contained +syn match fmq_listen_Directive "^\s*client_verification_ca_file\s" contained +syn match fmq_listen_Directive "^\s*client_verification_still_do_authn\s" contained +syn match fmq_listen_Directive "^\s*fullchain\s" contained +syn match fmq_listen_Directive "^\s*haproxy\s" contained +syn match fmq_listen_Directive "^\s*inet4_bind_address\s" contained +syn match fmq_listen_Directive "^\s*inet6_bind_address\s" contained +syn match fmq_listen_Directive "^\s*inet_protocol\s" contained +syn match fmq_listen_Directive "^\s*port\s" contained +syn match fmq_listen_Directive "^\s*privkey\s" contained +syn match fmq_listen_Directive "^\s*protocol\s" contained +syn match fmqTopLevelDirective "^\s*log_debug\s" +syn match fmqTopLevelDirective "^\s*log_file\s" +syn match fmqTopLevelDirective "^\s*log_level\s" +syn match fmqTopLevelDirective "^\s*log_subscriptions\s" +syn match fmqTopLevelDirective "^\s*max_event_loop_drift\s" +syn match fmqTopLevelDirective "^\s*max_incoming_topic_alias_value\s" +syn match fmqTopLevelDirective "^\s*max_outgoing_topic_alias_value\s" +syn match fmqTopLevelDirective "^\s*max_packet_size\s" +syn match fmqTopLevelDirective "^\s*minimum_wildcard_subscription_depth\s" +syn match fmqTopLevelDirective "^\s*mosquitto_acl_file\s" +syn match fmqTopLevelDirective "^\s*mosquitto_password_file\s" +syn match fmqTopLevelDirective "^\s*overload_mode\s" +syn match fmqTopLevelDirective "^\s*plugin\s" +syn match fmqTopLevelDirective "^\s*plugin_opt_\s" +syn match fmqTopLevelDirective "^\s*plugin_serialize_auth_checks\s" +syn match fmqTopLevelDirective "^\s*plugin_serialize_init\s" +syn match fmqTopLevelDirective "^\s*plugin_timer_period\s" +syn match fmqTopLevelDirective "^\s*quiet\s" +syn match fmqTopLevelDirective "^\s*retained_messages_delivery_limit\s" +syn match fmqTopLevelDirective "^\s*retained_messages_mode\s" +syn match fmqTopLevelDirective "^\s*retained_messages_node_limit\s" +syn match fmqTopLevelDirective "^\s*rlimit_nofile\s" +syn match fmqTopLevelDirective "^\s*shared_subscription_targeting\s" +syn match fmqTopLevelDirective "^\s*storage_dir\s" +syn match fmqTopLevelDirective "^\s*thread_count\s" +syn match fmqTopLevelDirective "^\s*websocket_set_real_ip_from\s" +syn match fmqTopLevelDirective "^\s*wildcard_subscription_deny_mode\s" +syn match fmqTopLevelDirective "^\s*wills_enabled\s" +syn match fmqTopLevelDirective "^\s*zero_byte_username_is_anonymous\s" + +" +" Global scopes +" + +syn region fmq_bridge_InnerBlock start="{" end="}" contains=fmq_bridge_Directive,fmqComment +syn region fmq_bridge_Block start="^bridge\s" end="$" contains=fmq_bridge_InnerBlock +hi link fmq_bridge_Directive Type +hi link fmq_bridge_Block Statement + +syn region fmq_listen_InnerBlock start="{" end="}" contains=fmq_listen_Directive,fmqComment +syn region fmq_listen_Block start="^listen\s" end="$" contains=fmq_listen_InnerBlock +hi link fmq_listen_Directive Type +hi link fmq_listen_Block Statement + diff --git a/man/vim/syntax.generate.sh b/man/vim/syntax.generate.sh new file mode 100755 index 00000000..bb3bca80 --- /dev/null +++ b/man/vim/syntax.generate.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -eu + +die() { + >&2 echo "Fatal error: $*" + exit 9 +} + +[[ $# -eq 2 ]] || die "Expected 2 arguments, got $#: $* +Usage: [ template file ] [ .dbk5 file ]" + +template="$1" +dbk5="$2" + +cat "$template" + +# Finding config options +xmlnames=$(sed -n '/^\s*.*/ { s//\1/; p }' "$dbk5" | sort) +readarray -t all_config_names <<< "$xmlnames" + +# This will contain listen, bridge etc. +declare -a GLOBAL_SCOPES=() + +echo "\"" +echo "\" Local scopes" +echo "\"" +echo "" + +for configname in "${all_config_names[@]}" +do + if [[ "$configname" == *"__"* ]]; + then + # this is a nested option like bridge__address + scope=${configname%__*} # bridge + localname=${configname#*__} # address + echo "syn match fmq_${scope}_Directive \"^\s*$localname\s\" contained" + GLOBAL_SCOPES+=("$scope") + + else + # this is a global option like log_file + echo "syn match fmqTopLevelDirective \"^\s*$configname\s\"" + fi +done + +readarray -t unique_global_scopes <<< "$(echo "${GLOBAL_SCOPES[*]}" | sed 's/ /\n/g' | sort | uniq)" + +echo "" +echo "\"" +echo "\" Global scopes" +echo "\"" +echo "" + +for scope in "${unique_global_scopes[@]}" +do + echo "syn region fmq_${scope}_InnerBlock start=\"{\" end=\"}\" contains=fmq_${scope}_Directive,fmqComment" + echo "syn region fmq_${scope}_Block start=\"^$scope\s\" end=\"$\" contains=fmq_${scope}_InnerBlock" + echo "hi link fmq_${scope}_Directive Type" + echo "hi link fmq_${scope}_Block Statement" + echo "" +done diff --git a/man/vim/syntax.template.vim b/man/vim/syntax.template.vim new file mode 100644 index 00000000..36dce5f0 --- /dev/null +++ b/man/vim/syntax.template.vim @@ -0,0 +1,42 @@ +" Vim syntax file +" Language: FlashMQ configuration file + +" +" Goals: +" +" Make incorrect / unknown options stand out. Special attention is given to +" also have this work correctly for blocks, E.G., the toplevel directive +" `log_file` is colored in a global scope but not when it's used inside a +" `listen` block +" +" https://vimdoc.sourceforge.net/htmldoc/syntax.html +" +" TODO: +" - Test number of arguments, specifically: most options take 1 argument, but +" fe. bridge__subscribe takes an optional second argument +" - Deal with quoted options? +" + +if exists("b:current_syntax") + finish +endif +let b:current_syntax = "flashmq" + +syn region fmqComment display oneline start='^\s*#' end='$' + +" We "render" fmqWrongBlock as Error (which makes it red), but then also use +" transparent which makes it be the color of the parent, so it all becomes a +" neutral color. +" Without the transparent+Error trick you would see fmqTopLevelDirective +" highlighted inside blocks which is undesirable: you can't specify `log_file` +" inside a `listen` block, so it shouldn't get colorized. +" +" Real blocks (like `listen` and `bridge`) are defined later and thus get a +" higher priority, and will replace this match. +syn region fmqWrongBlock start=+^.*{+ end=+}+ transparent contains=NONE,fmqComment + +hi link fmqComment Comment +hi link fmqWrongBlock Error +hi link fmqTopLevelDirective Type + +" The rest of this file has been dynamically generated