package-commands.lua 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. -- package-commands.lua
  2. -- Editor commands for the package manager
  3. -- ============================================================================
  4. -- Get package manager from lumacs global (set by init.lua)
  5. local package_manager = lumacs.package
  6. -- ============================================================================
  7. -- Package Commands
  8. -- ============================================================================
  9. -- List installed/loaded packages
  10. editor:register_command("package-list", "List loaded packages", function(args)
  11. local loaded = package_manager.list_loaded()
  12. if #loaded == 0 then
  13. return {success = true, message = "No packages loaded"}
  14. end
  15. local lines = {"Loaded packages:"}
  16. for _, pkg in ipairs(loaded) do
  17. local status = pkg.source == "builtin" and "[builtin]" or "[installed]"
  18. table.insert(lines, string.format(" %s %s", pkg.name, status))
  19. end
  20. editor:set_echo_area(lines)
  21. return {success = true, message = ""}
  22. end)
  23. -- Show package info
  24. editor:register_command("package-info", "Show information about a package", function(args)
  25. if #args == 0 then
  26. return {success = true, message = "Usage: package-info <name>"}
  27. end
  28. local name = args[1]
  29. local info = package_manager.info(name)
  30. local lines = {
  31. "Package: " .. name,
  32. "Status: " .. (info.loaded and "loaded" or "not loaded"),
  33. }
  34. if info.installed then
  35. table.insert(lines, "Source: " .. info.source)
  36. table.insert(lines, "Path: " .. info.path)
  37. else
  38. table.insert(lines, "Not installed locally")
  39. end
  40. if info.registry then
  41. table.insert(lines, "Registry: " .. (info.registry.description or "available"))
  42. if info.registry.url then
  43. table.insert(lines, "URL: " .. info.registry.url)
  44. end
  45. end
  46. editor:set_echo_area(lines)
  47. return {success = true, message = ""}
  48. end, {}, true, "s")
  49. -- Install a package
  50. editor:register_command("package-install", "Install a package", function(args)
  51. if #args == 0 then
  52. return {success = true, message = "Usage: package-install <name-or-url>"}
  53. end
  54. local spec = args[1]
  55. -- Check if it's a URL
  56. if spec:match("^https?://") or spec:match("^git@") then
  57. spec = { url = spec }
  58. end
  59. local ok, msg = package_manager.install(spec)
  60. if ok then
  61. -- Try to load it
  62. local load_ok, load_msg = package_manager.load_package(spec)
  63. if load_ok then
  64. return {success = true, message = msg .. " and loaded"}
  65. else
  66. return {success = true, message = msg .. " (not loaded: " .. load_msg .. ")"}
  67. end
  68. else
  69. return {success = false, message = msg}
  70. end
  71. end, {}, true, "s")
  72. -- Update a package
  73. editor:register_command("package-update", "Update a package", function(args)
  74. if #args == 0 then
  75. return {success = true, message = "Usage: package-update <name>"}
  76. end
  77. local name = args[1]
  78. local ok, msg = package_manager.update(name)
  79. return {success = ok, message = msg}
  80. end, {}, true, "s")
  81. -- Update all packages
  82. editor:register_command("package-update-all", "Update all installed packages", function(args)
  83. local loaded = package_manager.list_loaded()
  84. local updated = 0
  85. local failed = 0
  86. for _, pkg in ipairs(loaded) do
  87. if pkg.source == "installed" then
  88. local ok, msg = package_manager.update(pkg.name)
  89. if ok then
  90. updated = updated + 1
  91. else
  92. failed = failed + 1
  93. end
  94. end
  95. end
  96. return {success = true, message = string.format("Updated %d packages, %d failed", updated, failed)}
  97. end)
  98. -- Remove a package
  99. editor:register_command("package-remove", "Remove an installed package", function(args)
  100. if #args == 0 then
  101. return {success = true, message = "Usage: package-remove <name>"}
  102. end
  103. local name = args[1]
  104. -- Check if it's builtin
  105. local info = package_manager.info(name)
  106. if info.builtin then
  107. return {success = false, message = "Cannot remove builtin package: " .. name}
  108. end
  109. local ok, msg = package_manager.remove(name)
  110. return {success = ok, message = msg}
  111. end, {}, true, "s")
  112. -- Reload a package
  113. editor:register_command("package-reload", "Reload a package", function(args)
  114. if #args == 0 then
  115. return {success = true, message = "Usage: package-reload <name>"}
  116. end
  117. local name = args[1]
  118. -- Clear from loaded
  119. package_manager.loaded[name] = nil
  120. package_manager.packages[name] = nil
  121. -- Reload
  122. local ok, msg = package_manager.load_package(name)
  123. return {success = ok, message = ok and "Reloaded " .. name or msg}
  124. end, {}, true, "s")
  125. -- Update registry
  126. editor:register_command("package-refresh", "Refresh package registry from remote", function(args)
  127. local ok = package_manager.update_registry()
  128. if ok then
  129. local count = 0
  130. for _ in pairs(package_manager.registry) do count = count + 1 end
  131. return {success = true, message = "Registry updated: " .. count .. " packages available"}
  132. else
  133. return {success = false, message = "Failed to update registry"}
  134. end
  135. end)
  136. -- Search packages in registry
  137. editor:register_command("package-search", "Search for packages in registry", function(args)
  138. if #args == 0 then
  139. -- List all available
  140. local count = 0
  141. local lines = {"Available packages:"}
  142. for name, entry in pairs(package_manager.registry) do
  143. local desc = entry.description or ""
  144. if #desc > 40 then desc = desc:sub(1, 37) .. "..." end
  145. table.insert(lines, string.format(" %s - %s", name, desc))
  146. count = count + 1
  147. end
  148. if count == 0 then
  149. return {success = true, message = "No packages in registry. Run package-refresh first."}
  150. end
  151. table.sort(lines)
  152. editor:set_echo_area(lines)
  153. return {success = true, message = ""}
  154. end
  155. local query = args[1]:lower()
  156. local matches = {}
  157. for name, entry in pairs(package_manager.registry) do
  158. local desc = (entry.description or ""):lower()
  159. if name:lower():find(query) or desc:find(query) then
  160. table.insert(matches, {name = name, description = entry.description or ""})
  161. end
  162. end
  163. if #matches == 0 then
  164. return {success = true, message = "No packages matching: " .. query}
  165. end
  166. local lines = {"Matching packages:"}
  167. for _, m in ipairs(matches) do
  168. local desc = m.description
  169. if #desc > 40 then desc = desc:sub(1, 37) .. "..." end
  170. table.insert(lines, string.format(" %s - %s", m.name, desc))
  171. end
  172. editor:set_echo_area(lines)
  173. return {success = true, message = ""}
  174. end, {}, true, "s")
  175. -- Sync packages (install missing from packages.lua)
  176. editor:register_command("package-sync", "Sync packages from packages.lua", function(args)
  177. local installed = 0
  178. local failed = 0
  179. local skipped = 0
  180. for _, spec in ipairs(package_manager.package_specs) do
  181. local normalized = package_manager.normalize_spec and package_manager.normalize_spec(spec)
  182. if not normalized then
  183. -- Try to normalize manually
  184. if type(spec) == "string" then
  185. normalized = {name = spec, source = "name"}
  186. elseif type(spec) == "table" and spec[1] then
  187. normalized = {name = spec[1], source = "name"}
  188. end
  189. end
  190. if normalized and normalized.enabled ~= false then
  191. local info = package_manager.info(normalized.name)
  192. if not info.installed then
  193. local ok, msg = package_manager.install(spec)
  194. if ok then
  195. installed = installed + 1
  196. else
  197. failed = failed + 1
  198. end
  199. else
  200. skipped = skipped + 1
  201. end
  202. end
  203. end
  204. return {success = true, message = string.format("Sync complete: %d installed, %d failed, %d already present", installed, failed, skipped)}
  205. end)
  206. -- Create sample packages.lua
  207. editor:register_command("package-init", "Create a sample packages.lua file", function(args)
  208. local packages_file = os.getenv("HOME") .. "/.lumacs/packages.lua"
  209. -- Check if exists
  210. local f = io.open(packages_file, "r")
  211. if f then
  212. f:close()
  213. return {success = false, message = "packages.lua already exists at ~/.lumacs/packages.lua"}
  214. end
  215. -- Create directory
  216. os.execute("mkdir -p " .. os.getenv("HOME") .. "/.lumacs")
  217. -- Write sample file
  218. local sample = [[
  219. -- Lumacs Package Configuration
  220. -- ============================================================================
  221. -- Add packages here. They will be automatically installed and loaded.
  222. --
  223. -- Formats:
  224. -- "package-name" -- From registry
  225. -- { "package-name", lazy = true } -- Lazy loaded
  226. -- { url = "https://github.com/user/pkg" } -- Git URL
  227. -- { dir = "~/my-package" } -- Local directory
  228. -- ============================================================================
  229. return {
  230. -- Example packages (uncomment to enable):
  231. -- "evil-mode", -- Vim emulation
  232. -- "magit", -- Git interface
  233. -- Example with config:
  234. -- { "which-key",
  235. -- config = function()
  236. -- require("which-key").setup({ delay = 500 })
  237. -- end
  238. -- },
  239. }
  240. ]]
  241. local file = io.open(packages_file, "w")
  242. if file then
  243. file:write(sample)
  244. file:close()
  245. return {success = true, message = "Created ~/.lumacs/packages.lua"}
  246. else
  247. return {success = false, message = "Failed to create packages.lua"}
  248. end
  249. end)
  250. -- ============================================================================
  251. -- Keybindings
  252. -- ============================================================================
  253. -- No default keybindings - access via M-x
  254. print("[Lua] package-commands loaded")