[AHK2] 合并使用#include的脚本

发布时间 2023-10-22 18:15:37作者: 落寞的雪

这个脚本用于将一个脚本中的#include语句包含的脚本添加到这条#include语句的位置。
同时,它有其他功能,如:去除空行、注释(仅单行)、替换内置变量。
因为脚本原理是读取单行并处理,所以只能处理单行注释,要做更多复杂功能就需要使用其他方法了,比如索引表……
但脚本主要目的就是合并分部分的脚本,方便编译成exe。

; ========
; by wtdkwd 2023/10/21
; ========
; include files see `https://gitee.com/dkwd/ahk.git`
#Requires AutoHotkey v2.0
#SingleInstance Force

#Include 'G:\AHK\gitee_ahk2\common\Extend.ahk'
#Include 'G:\AHK\gitee_ahk2\common\Path.ahk'

require := Map()                              ; store the require statements
directives := Map()
included := Map()

buildin := Map()

requiresRE := 'i)#requires\s(.*)'
includedRE := 'i)#include\s[`'|"](.*)[`'|"]'
commentRE := '\s*;.*$'                        ; This RE only handles single-line comments
trailingComment := '^(.*?)\s+;.*?$'           ; trailingComment

RemoveComment(&line) {                        ; Remove the comment
  copy := line
  if copy ~= commentRE {
    copy := ''
  } else if RegExMatch(copy, trailingComment, &match) {
    copy := match[1]
  } else {
    copy := copy
  }
  line := copy
}

ReplaceKeyWord(&input) {                      ; Remove the buildin keyworlds
  copy := input
  for k, v in buildin
    copy := StrReplace(copy, k, v)
  input := copy
}

_Trim(&input) {                               ; Remove before and after Spaces
  copy := input
  input := Trim(copy)
}

filters := [_Trim, RemoveComment, ReplaceKeyWord]

_Trim_(singleLine) {
  for fn in filters {                         ; Run filter
    fn(&singleLine)
  }
  return singleLine ? singleLine '`r`n' : ''
}

ConcatPath(curr, _path) {
  Path.Normalize(&curr)
  Path.Normalize(&_path)
  segs := []

  curr := StrSplit(curr, Path.Sep)
  _path := StrSplit(_path, Path.Sep)

  loop _path.Length {
    if '..' == _path[A_Index] {
      curr.Pop()
    } else if '.' != _path[A_Index] && '' != _path[A_Index] {
      segs.Push(_path[A_Index])
    }
  }
  return curr.Concat(segs).Join(Path.Sep)
}

Reset() {
  global
  require.Clear()
  directives.Clear()
  included.Clear()
}

Resolve(fileFullPath) {
  resolved := ''
  global currentFile := fileFullPath
  currentDir := Path.ParseFilePath(fileFullPath).dir
  f := FileOpen(fileFullPath, 'r', 'utf-8')
  while !f.AtEOF {
    oriLine := f.ReadLine()
    if !oriLine                               ; brank line
      continue
    line := _Trim_(oriLine)
    d.Join line, oriLine
    if !line                                  ; brank line
      continue
    if line ~= '^#' {
      if line ~= requiresRE {
        RegExMatch(line, requiresRE, &match)
        content := match[1]
        if require.Has(content)
          line := ''
        require.set(fileFullPath, content)
      } else if line ~= includedRE {
        RegExMatch(line, includedRE, &match)
        includeFilePath := match[1]
        if Path.IsAbsolute(includeFilePath) { ; If it is an absolute path, no processing is needed
          resolvedPath := includeFilePath
        } else {                              ; Get the correct absolute path
          resolvedPath := ConcatPath(currentDir, includeFilePath)
        }
        if not included.Has(resolvedPath) {
          included.Set(resolvedPath, true)
          resolved .= Resolve(resolvedPath)   ; Recursive processing
        }
        line := ''
      } else {
        directives.Set(fileFullPath, line)
      }
    }
    resolved .= line
  }
  return resolved
}

#Include 'G:\AHK\gitee_ahk2\common\debug.ahk'
d := Debug('Merge.ahk - A tool for merging partial ahk scripts  -- Drag the file you want to merge into the gui', 1600, 800, , , , 'DETAIL', false)

d.Log()
d.OnEvent('DropFiles', OnDropFiles)

OnDropFiles(GuiObj, GuiCtrlObj, FileArray, X, Y) {
  d.ClearContent()
  Reset()
  DroppedFile := FileArray[1]
  parsed := Path.ParseFilePath(DroppedFile)
  output := parsed.dir Path.Sep parsed.fileName '_Merged.' parsed.ext       ; the output path
  global currentFile := DroppedFile
  global buildin
  buildin.Set('A_LineFile', "'" currentFile "'")                            ; keywords
  prefix := A_Space.repeat(14)
  d.Join 'filter count: '
  d.Join prefix filters.Length
  d.Join 'filter funcs: ', 'function name'
  for v in filters
    d.Join prefix v.Name
  d.Join 'buildin info: ', 'The keyword to replace'
  for k, v in buildin
    d.Join prefix k
  d.Divi 'RESOLVE'
  result := Resolve(DroppedFile)
  d.Divi('RESULT', , true)
  subStrings := StrSplit(result, '`r`n')
  for v in subStrings
    d.Join v, '-'
  d.Divi('REQUIRES')
  for k, v in require
    d.Join v, k
  d.Divi('DIRECTIVES')
  for k, v in directives
    d.Join v, k
  d.Divi('INCLUDE')
  for k, v in included
    d.Join k
  d.Log()

  f := FileOpen(output, 'w', 'utf-8')
  f.Write(result)
  f.Close()

  MsgBox 'The merged files have been saved to: ' output
}