1
0

extension.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package editor
  2. import (
  3. "embed"
  4. "fmt"
  5. "path/filepath"
  6. "strings"
  7. lua "github.com/yuin/gopher-lua"
  8. )
  9. type ExtensionManager struct {
  10. L *lua.LState
  11. M *Model
  12. LoadedFiles []string
  13. currentDiskDir string
  14. currentEmbedDir string
  15. }
  16. //go:embed lua
  17. var embeddedScripts embed.FS
  18. func NewExtensionManager(m *Model) *ExtensionManager {
  19. em := &ExtensionManager{
  20. L: lua.NewState(),
  21. M: m,
  22. }
  23. lua.OpenPackage(em.L)
  24. em.registerAPI()
  25. em.registerExtensionSearcher()
  26. if err := em.LoadEmbeddedFile("init.lua"); err != nil {
  27. m.Mode = ModeNormal
  28. m.BM.PaletteBuffer.Clear()
  29. m.BM.PaletteBuffer.Insert("moose.error:Lua error " + err.Error())
  30. }
  31. if err := em.LoadFile("/home/johron/.config/moose/moose.lua"); err != nil {
  32. m.Mode = ModeNormal
  33. m.BM.PaletteBuffer.Clear()
  34. m.BM.PaletteBuffer.Insert("moose.error:Lua error " + err.Error())
  35. }
  36. return em
  37. }
  38. func (em *ExtensionManager) registerExtensionSearcher() {
  39. if em.L == nil {
  40. return
  41. }
  42. pkg := em.L.GetGlobal("package")
  43. if pkg.Type() == lua.LTNil {
  44. return
  45. }
  46. loadersVal := em.L.GetField(pkg, "loaders")
  47. packageLoaders, ok := loadersVal.(*lua.LTable)
  48. if !ok {
  49. return
  50. }
  51. extensionSearcher := em.L.NewFunction(func(L *lua.LState) int {
  52. modName := L.CheckString(1)
  53. fileName := strings.ReplaceAll(modName, ".", "/") + ".lua"
  54. var errorsLogged []string
  55. if em.currentDiskDir != "" {
  56. targetDiskFile := filepath.Join(em.currentDiskDir, fileName)
  57. if fn, loadErr := L.LoadFile(targetDiskFile); loadErr == nil {
  58. return pushAndReturn(L, fn)
  59. } else {
  60. errorsLogged = append(errorsLogged, fmt.Sprintf("no relative file: %s", targetDiskFile))
  61. }
  62. }
  63. if em.currentEmbedDir != "" {
  64. targetEmbedFile := filepath.Join(em.currentEmbedDir, fileName)
  65. if bytes, err := embeddedScripts.ReadFile(targetEmbedFile); err == nil {
  66. if fn, err := L.LoadString(string(bytes)); err == nil {
  67. return pushAndReturn(L, fn)
  68. } else {
  69. L.RaiseError("failed to compile embedded module %s: %v", modName, err)
  70. return 0
  71. }
  72. } else {
  73. errorsLogged = append(errorsLogged, fmt.Sprintf("no relative embed file: %s", targetEmbedFile))
  74. }
  75. }
  76. L.Push(lua.LString("\n\t" + strings.Join(errorsLogged, "\n\t")))
  77. return 1
  78. })
  79. idx2 := packageLoaders.RawGetInt(2)
  80. idx3 := packageLoaders.RawGetInt(3)
  81. idx4 := packageLoaders.RawGetInt(4)
  82. packageLoaders.RawSetInt(2, extensionSearcher)
  83. packageLoaders.RawSetInt(3, idx2)
  84. packageLoaders.RawSetInt(4, idx3)
  85. if idx4 != lua.LNil {
  86. packageLoaders.RawSetInt(5, idx4)
  87. }
  88. }
  89. func pushAndReturn(L *lua.LState, fn *lua.LFunction) int {
  90. L.Push(fn)
  91. return 1
  92. }
  93. func (em *ExtensionManager) Close() {
  94. if em.L != nil {
  95. em.L.Close()
  96. em.L = nil
  97. }
  98. }
  99. func (em *ExtensionManager) registerAPI() {
  100. if em.L == nil {
  101. return
  102. }
  103. moose := em.L.NewTable()
  104. em.L.SetGlobal("ms", moose)
  105. moose.RawSetString("config", GetConfigTable(em))
  106. }
  107. func (em *ExtensionManager) LoadFile(path string) error {
  108. oldDiskDir := em.currentDiskDir
  109. em.currentDiskDir = filepath.Dir(path)
  110. defer func() { em.currentDiskDir = oldDiskDir }()
  111. if err := em.L.DoFile(path); err != nil {
  112. return err
  113. }
  114. em.LoadedFiles = append(em.LoadedFiles, path)
  115. return nil
  116. }
  117. func (em *ExtensionManager) LoadEmbeddedFile(path string) error {
  118. if em.L == nil {
  119. return fmt.Errorf("lua state is closed")
  120. }
  121. embedPath := filepath.Join("lua", path)
  122. bytes, err := embeddedScripts.ReadFile(embedPath)
  123. if err != nil {
  124. return fmt.Errorf("failed to read embedded script %s: %w", path, err)
  125. }
  126. oldEmbedDir := em.currentEmbedDir
  127. em.currentEmbedDir = filepath.Dir(embedPath)
  128. defer func() { em.currentEmbedDir = oldEmbedDir }()
  129. if err := em.L.DoString(string(bytes)); err != nil {
  130. return err
  131. }
  132. em.LoadedFiles = append(em.LoadedFiles, path)
  133. return nil
  134. }
  135. func (em *ExtensionManager) LoadString(name string, src string) error {
  136. if em.L == nil {
  137. return fmt.Errorf("lua state is closed")
  138. }
  139. if err := em.L.DoString(src); err != nil {
  140. return err
  141. }
  142. em.LoadedFiles = append(em.LoadedFiles, name)
  143. return nil
  144. }