1
0

extension.go 4.1 KB

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