forked from neo/neo-layout
208 lines
6.5 KiB
AutoHotkey
208 lines
6.5 KiB
AutoHotkey
; Generate script with compose-definitions from XCompose files
|
|
;
|
|
; Usage:
|
|
; Run as standalone script with AutoHotkey.exe.
|
|
;
|
|
; Generates the AHK-source-script which sets up the available compose-
|
|
; sequences in the neovars driver.
|
|
;
|
|
; Compose-sequences must be provided in input files, whose names along
|
|
; with other configuration are read from "..\config\compose-config.ini",
|
|
;
|
|
; The output script is written to "..\src\compos.generated.ahk", if all
|
|
; input files reside inside the neovars git-repository and have no uncommitted
|
|
; changes; or "..\src\composee-tainted.generated.ahk" otherwise.
|
|
; If either output script exist, it will be removed. Depending on
|
|
; configuration options, a backup copy is made (overwriting existing backups).
|
|
;
|
|
; Only a subset of the XCompose syntax is supported. For instance,
|
|
; input files shall not define includes, and must not specify modifiers.
|
|
; See also https://www.x.org/releases/X11R7.7/doc/man/man5/Compose.5.xhtml
|
|
|
|
|
|
FileEncoding, UTF-8
|
|
|
|
logInit("Compose-update")
|
|
SetWorkingDir %A_ScriptDir%
|
|
#Include %A_ScriptDir%\
|
|
#Include script/logwindow.ahk
|
|
#Include script/util.ahk
|
|
#include ../src/compose-parse.ahk
|
|
#include ../src/util.ahk
|
|
|
|
; Paths
|
|
srcDir := A_ScriptDir . "\..\src"
|
|
binDir := A_ScriptDir . "\..\bin"
|
|
configDir := A_ScriptDir . "\..\config"
|
|
iniFile := util_getFullPath(configDir . "\compose-config.ini")
|
|
|
|
;
|
|
; Read user-defined configuration
|
|
;
|
|
|
|
; Default configs (keys are not case-sensitive)
|
|
userConfigDefaults := {composeFilesPrefix: "", composeFiles: "", makeBackups: True}
|
|
configs := {revision: "<unknown>", composeFilesFull: [], outputFile: ""}
|
|
|
|
; Read user-defined configs from ini-file
|
|
if not FileExist(iniFile)
|
|
{
|
|
logError("Configuration file not found at '" . iniFile . "'")
|
|
logFinal()
|
|
}
|
|
logEntry("Read user-defined configuration...")
|
|
for key, value in userConfigDefaults
|
|
{
|
|
IniRead, valueNew, %iniFile%, Global, %key% , %value%
|
|
configs[key] := valueNew
|
|
}
|
|
|
|
; Asseble full pathnames for input files
|
|
composeFilesCSV := configs["composeFiles"]
|
|
Loop, parse, composeFilesCSV, CSV, %A_Space%%A_Tab%
|
|
{
|
|
configs["composeFilesFull"].push(util_getFullPath(configs["composeFilesPrefix"] . A_LoopField))
|
|
}
|
|
numComposeFiles := configs["composeFilesFull"].Length()
|
|
if (numComposeFiles == 0)
|
|
logWarning("No compose-files provided! Compose-definitions will be empty.")
|
|
|
|
; Validate input filenames (early exit on errors)
|
|
Loop, % numComposeFiles
|
|
{
|
|
file := configs["composeFilesFull"][A_Index]
|
|
if not FileExist(file)
|
|
{
|
|
logError("Compose-file not found at '" . file . "'")
|
|
logFinal()
|
|
}
|
|
}
|
|
|
|
;
|
|
; Other configurations
|
|
;
|
|
|
|
; Query revsion information for the compose files (requires git)
|
|
; If no revision can be determined (e.g. git not installed or input-
|
|
; files from outside the repository), a warning will be printed.
|
|
logEntry("Query revision information...")
|
|
composePathsQuoted := ""
|
|
for i, file in configs["composeFilesFull"]
|
|
composePathsQuoted .= """" . file . """ "
|
|
exitCode := util_runWaitCMD("git rev-list -n 1 HEAD -- " . composePathsQuoted, strOut, strErr)
|
|
if not exitCode and StrLen(strOut) >= 7
|
|
configs["revision"] := SubStr(strOut, 1, 7)
|
|
else
|
|
logWarning("Could not query revision information from git repository. " . strErr)
|
|
|
|
; Clear any existing output files (if exist)
|
|
logEntry("Deleting old compose scripts")
|
|
outputFileDefault := util_getFullPath(srcDir . "\compose.generated.ahk")
|
|
outputFileTainted := util_getFullPath(srcDir . "\compose-tainted.generated.ahk")
|
|
for i,f in [outputFileDefault, outputFileTainted]
|
|
{
|
|
; Move existing scripts to backup copy (overwrite existing backups)
|
|
if (configs["makeBackups"])
|
|
{
|
|
backupFile := f . "~"
|
|
FileMove, % f, % backupFile, 1
|
|
}
|
|
else
|
|
FileDelete, % f
|
|
if FileExist(f)
|
|
{
|
|
logError("Could not remove old compose script '" . f . "'")
|
|
logFinal()
|
|
}
|
|
}
|
|
|
|
; Name of the output script (in source-directory):
|
|
; - If uncommitted changes pending: (over-)write to "compose-tainted.generated.ahk"
|
|
; - If working-dir clean: (over-)write to "compose.generated.ahk"
|
|
exitCode := util_runWaitCMD("git diff --quiet -- " . composePathsQuoted)
|
|
configs["outputFile"] := (exitCode ? outputFileTainted : outputFileDefault)
|
|
logEntry("Set output file to '" . configs["outputFile"] . "'")
|
|
|
|
;
|
|
; Parse compose-definitions into source script
|
|
;
|
|
|
|
; Write header
|
|
logEntry("Write header information.")
|
|
scriptGeneratedBy := A_ScriptName
|
|
scriptLineRevision := "compRevision := """ . configs["revision"] . """"
|
|
FileAppend,
|
|
(
|
|
; -*- encoding: utf-8 -*-
|
|
;
|
|
; ** THIS FILE WAS GENERATED BY %scriptGeneratedBy% **
|
|
; ** DO NOT EDIT BY HAND -- FILE MAY BE OVERWRITTEN ANYTIME **
|
|
;
|
|
|
|
; Revision information
|
|
%scriptLineRevision%
|
|
|
|
; Make compose-definitions globally available
|
|
LoadDefaultCompose() {
|
|
global
|
|
|
|
), % configs["outputFile"], UTF-8
|
|
|
|
; Write compose-definitions
|
|
onParseErrorFun := Func("onParseError")
|
|
onProgressFun := Func("onProgress")
|
|
keySym2KeyIdMap := makekeySym2KeyIdMap()
|
|
totalErrorCount := 0
|
|
Loop, % numComposeFiles
|
|
{
|
|
file := configs["composeFilesFull"][A_Index]
|
|
logEntry("Parse compose-definitions from '" . file . "'...")
|
|
if not FileExist(file)
|
|
{
|
|
logError("Compose-file not found at '" . file . "'")
|
|
continue ; try to complete output with footer etc.
|
|
}
|
|
try
|
|
totalErrorCount += parseComposeFile(file, keySym2KeyIdMap, configs["outputFile"], onParseErrorFun, onProgressFun)
|
|
catch e
|
|
{
|
|
logError("Unhandled exception '" . e.What . (e.Message == "" ? "" : "' reports '" . e.Message) . "' while processing '" . file . "'")
|
|
continue ; try to complete output with footer etc.
|
|
}
|
|
}
|
|
logEntry(Format("Parsing finished with {:i} parsing-error{:s} in {:i} file{:s}.", totalErrorCount, util_pluralS(totalErrorCount), numComposeFiles, util_pluralS(numComposeFiles)))
|
|
|
|
; Write footer
|
|
logEntry("Write footer information.")
|
|
FileAppend, }, % configs["outputFile"], UTF-8
|
|
|
|
; Exit
|
|
logEntry("Compose-update completed.")
|
|
logFinal()
|
|
|
|
|
|
; -------------------------------------
|
|
; Functions
|
|
; -------------------------------------
|
|
|
|
; Compose-Parser progress handler
|
|
onParseError(e, errorCount)
|
|
{
|
|
if (e.type == "key_sym")
|
|
logWarning(Format("Ignored unknown <{:s}> on line {:u} '{:s}'", e.keySym, e.lineNo, e.line))
|
|
else if (e.type == "result")
|
|
logWarning(Format("Ignored empty result on line {:u} '{:s}'", e.lineNo, e.line))
|
|
else
|
|
logWarning(Format("Ignored bad line {:u} '{:s}'", e.lineNo, e.line))
|
|
}
|
|
|
|
; Compose-Parser error handler
|
|
onProgress(count, total, errorCount)
|
|
{
|
|
progress := 100 * count / total
|
|
msgStr := Format("complete - {:u} line{:s} with {:u} parsing-error{:s}", count, util_pluralS(count), errorCount, util_pluralS(errorCount))
|
|
logProgressUpdate(progress, msgStr)
|
|
if (count == total)
|
|
logProgressStop()
|
|
}
|