extension.go 4.0 KB

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