action.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. package editor
  2. import(
  3. "strings"
  4. "strconv"
  5. "slices"
  6. "os"
  7. "github.com/zyedidia/rope"
  8. "golang.design/x/clipboard"
  9. "moose/internal/util"
  10. )
  11. type ActionManager = struct {
  12. Common []Action
  13. Normal []Action
  14. Insert []Action
  15. Palette []Action
  16. }
  17. type Action = struct {
  18. Bindings []Binding
  19. Commands []string
  20. AskArgs bool
  21. Callback func(*Model, []string)
  22. }
  23. type Binding = struct {
  24. Type BindingType
  25. Single string
  26. Chord []string
  27. }
  28. type BindingType int
  29. const (
  30. BindingSingle BindingType = iota
  31. BindingChord
  32. )
  33. func DefaultActionManager() ActionManager {
  34. return ActionManager{
  35. Common: []Action{
  36. Action{
  37. Bindings: []Binding{
  38. Binding{
  39. Type: BindingSingle,
  40. Single: "f12",
  41. },
  42. },
  43. Callback: func(m *Model, args []string) {
  44. m.Mode = ModeNormal
  45. },
  46. },
  47. Action{
  48. Bindings: []Binding{
  49. Binding{
  50. Type: BindingSingle,
  51. Single: "ctrl+q",
  52. },
  53. },
  54. Commands: []string{"q", "quit"},
  55. AskArgs: true,
  56. Callback: func(m *Model, args []string) {
  57. m.Quit()
  58. },
  59. },
  60. Action{
  61. Bindings: []Binding{
  62. Binding{
  63. Type: BindingSingle,
  64. Single: "ctrl+s",
  65. },
  66. },
  67. Commands: []string{"s", "save"},
  68. AskArgs: false,
  69. Callback: func(m *Model, args []string) {
  70. path := ""
  71. if len(args) < 1 || args[0] == "" {
  72. if m.BM.Current().Path == "" {
  73. m.Mode = ModeNormal
  74. m.BM.PaletteBuffer.Clear()
  75. m.BM.PaletteBuffer.Insert("moose.error:Missing filename argument for save")
  76. return
  77. }
  78. }
  79. if len(args) > 0 && args[0] != "" {
  80. m.BM.Current().Path = args[0]
  81. }
  82. path = m.BM.Current().Path
  83. data := []byte(m.BM.Current().String())
  84. err := os.WriteFile(path, data, 0644)
  85. if err != nil {
  86. m.Mode = ModeNormal
  87. m.BM.PaletteBuffer.Clear()
  88. m.BM.PaletteBuffer.Insert("moose.error:Could not save to file \"" + path + "\": " + err.Error())
  89. return
  90. }
  91. m.BM.PaletteBuffer.Clear()
  92. m.BM.PaletteBuffer.Insert("moose.info:Wrote " + strconv.Itoa(len(data)) + " bytes to \"" + path + "\"")
  93. return
  94. },
  95. },
  96. Action{
  97. Bindings: []Binding{
  98. Binding{
  99. Type: BindingSingle,
  100. Single: "ctrl+shift+s",
  101. },
  102. },
  103. Commands: []string{"s", "save"},
  104. AskArgs: true,
  105. },
  106. Action{
  107. Bindings: []Binding{
  108. Binding{
  109. Type: BindingSingle,
  110. Single: "ctrl+e",
  111. },
  112. },
  113. Commands: []string{"e", "edit"},
  114. AskArgs: true,
  115. Callback: func(m *Model, args []string) {
  116. if len(args) < 1 {
  117. m.Mode = ModeNormal
  118. m.BM.PaletteBuffer.Clear()
  119. m.BM.PaletteBuffer.Insert("moose.error:Did not specify path for edit")
  120. return
  121. }
  122. content, err := os.ReadFile(args[0])
  123. if err != nil {
  124. m.Mode = ModeNormal
  125. m.BM.PaletteBuffer.Clear()
  126. m.BM.PaletteBuffer.Insert("moose.error:Could not edit file \"" + args[0] + "\": " + err.Error())
  127. return
  128. }
  129. m.BM.Current().Clear()
  130. m.BM.Current().Rope = rope.New(content)
  131. m.BM.Current().LI.Rebuild(m.BM.Current().Rope)
  132. m.BM.Current().Path = args[0]
  133. },
  134. },
  135. Action{
  136. Bindings: []Binding{
  137. Binding{
  138. Type: BindingSingle,
  139. Single: "left",
  140. },
  141. },
  142. Callback: func(m *Model, args []string) {
  143. if m.Mode == ModePalette {
  144. m.BM.PaletteBuffer.MoveHoriz(-1)
  145. } else {
  146. m.BM.Current().MoveHoriz(-1)
  147. }
  148. },
  149. },
  150. Action{
  151. Bindings: []Binding{
  152. Binding{
  153. Type: BindingSingle,
  154. Single: "right",
  155. },
  156. },
  157. Callback: func(m *Model, args []string) {
  158. if m.Mode == ModePalette {
  159. m.BM.PaletteBuffer.MoveHoriz(1)
  160. } else {
  161. m.BM.Current().MoveHoriz(1)
  162. }
  163. },
  164. },
  165. Action{
  166. Bindings: []Binding{
  167. Binding{
  168. Type: BindingSingle,
  169. Single: "up",
  170. },
  171. },
  172. Callback: func(m *Model, args []string) {
  173. if m.Mode != ModePalette {
  174. m.BM.Current().MoveVert(-1)
  175. }
  176. },
  177. },
  178. Action{
  179. Bindings: []Binding{
  180. Binding{
  181. Type: BindingSingle,
  182. Single: "down",
  183. },
  184. },
  185. Callback: func(m *Model, args []string) {
  186. if m.Mode != ModePalette {
  187. m.BM.Current().MoveVert(1)
  188. }
  189. },
  190. },
  191. Action{
  192. Bindings: []Binding{
  193. Binding{
  194. Type: BindingSingle,
  195. Single: "shift+left",
  196. },
  197. },
  198. AskArgs: false,
  199. Callback: func(m *Model, args []string) {
  200. if m.Mode == ModePalette {
  201. m.BM.PaletteBuffer.MoveWordHoriz(-1)
  202. } else {
  203. m.BM.Current().MoveWordHoriz(-1)
  204. }
  205. },
  206. },
  207. Action{
  208. Bindings: []Binding{
  209. Binding{
  210. Type: BindingSingle,
  211. Single: "shift+right",
  212. },
  213. },
  214. AskArgs: false,
  215. Callback: func(m *Model, args []string) {
  216. if m.Mode == ModePalette {
  217. m.BM.PaletteBuffer.MoveWordHoriz(1)
  218. } else {
  219. m.BM.Current().MoveWordHoriz(1)
  220. }
  221. },
  222. },
  223. },
  224. Normal: []Action{
  225. Action{
  226. Bindings: []Binding{
  227. Binding{
  228. Type: BindingSingle,
  229. Single: "w",
  230. },
  231. },
  232. Callback: func(m *Model, args []string) {
  233. m.Mode = ModeWrite
  234. },
  235. },
  236. Action{
  237. Bindings: []Binding{
  238. Binding{
  239. Type: BindingSingle,
  240. Single: "q",
  241. },
  242. },
  243. Callback: func(m *Model, args []string) {
  244. m.Mode = ModePalette
  245. m.BM.PaletteBuffer.Clear()
  246. m.BM.PaletteBuffer.Insert("/")
  247. },
  248. },
  249. Action{
  250. Bindings: []Binding{
  251. Binding{
  252. Type: BindingSingle,
  253. Single: "f",
  254. },
  255. },
  256. AskArgs: true,
  257. Callback: func(m *Model, args []string) {
  258. m.Mode = ModePalette
  259. m.BM.PaletteBuffer.Clear()
  260. m.BM.PaletteBuffer.Insert("?")
  261. },
  262. },
  263. Action{
  264. Bindings: []Binding{
  265. Binding{
  266. Type: BindingSingle,
  267. Single: "v",
  268. },
  269. },
  270. Commands: []string{"p", "paste"},
  271. AskArgs: false,
  272. Callback: func(m *Model, args []string) {
  273. text := clipboard.Read(clipboard.FmtText)
  274. if string(text) != "" {
  275. if m.Mode == ModePalette {
  276. m.BM.PaletteBuffer.Insert(string(text))
  277. } else {
  278. m.BM.Current().Insert(string(text))
  279. }
  280. }
  281. },
  282. },
  283. Action{
  284. Bindings: []Binding{
  285. Binding{
  286. Type: BindingSingle,
  287. Single: "j",
  288. },
  289. },
  290. Callback: func(m *Model, args []string) {
  291. if m.Mode == ModePalette {
  292. m.BM.PaletteBuffer.MoveHoriz(-1)
  293. } else {
  294. m.BM.Current().MoveHoriz(-1)
  295. }
  296. },
  297. },
  298. },
  299. Insert: []Action{
  300. Action{
  301. Bindings: []Binding{
  302. Binding{
  303. Type: BindingSingle,
  304. Single: "shift+up",
  305. },
  306. },
  307. Commands: []string{"TODO:"},
  308. AskArgs: false,
  309. Callback: func(m *Model, args []string) {
  310. m.BM.Current().AddCursorVert(-1)
  311. },
  312. },
  313. Action{
  314. Bindings: []Binding{
  315. Binding{
  316. Type: BindingSingle,
  317. Single: "shift+down",
  318. },
  319. },
  320. Commands: []string{"TODO:"},
  321. AskArgs: false,
  322. Callback: func(m *Model, args []string) {
  323. m.BM.Current().AddCursorVert(1)
  324. },
  325. },
  326. Action{
  327. Bindings: []Binding{
  328. Binding{
  329. Type: BindingSingle,
  330. Single: "esc",
  331. },
  332. },
  333. Commands: []string{"clrc", "clearcursors"},
  334. AskArgs: false,
  335. Callback: func(m *Model, args []string) {
  336. m.BM.Current().ClearCursors()
  337. },
  338. },
  339. Action{
  340. Bindings: []Binding{
  341. Binding{
  342. Type: BindingSingle,
  343. Single: "tab",
  344. },
  345. },
  346. Commands: []string{"TODO:"},
  347. AskArgs: false,
  348. Callback: func(m *Model, args []string) {
  349. for _ = range 4 {
  350. m.BM.Current().Insert(" ")
  351. }
  352. },
  353. },
  354. Action{
  355. Bindings: []Binding{
  356. Binding{
  357. Type: BindingSingle,
  358. Single: "shift+tab",
  359. },
  360. },
  361. Commands: []string{"TODO:"},
  362. AskArgs: false,
  363. Callback: func(m *Model, args []string) {
  364. // TODO: implement m.BM.Current().remove...
  365. },
  366. },
  367. Action{
  368. Bindings: []Binding{
  369. Binding{
  370. Type: BindingSingle,
  371. Single: "backspace",
  372. },
  373. },
  374. Callback: func(m *Model, args []string) {
  375. m.BM.Current().Delete()
  376. },
  377. },
  378. Action{
  379. Bindings: []Binding{
  380. Binding{
  381. Type: BindingSingle,
  382. Single: "enter",
  383. },
  384. },
  385. Callback: func(m *Model, args []string) {
  386. m.BM.Current().Insert("\n")
  387. },
  388. },
  389. },
  390. Palette: []Action{
  391. Action{
  392. Bindings: []Binding{
  393. Binding{
  394. Type: BindingSingle,
  395. Single: "backspace",
  396. },
  397. },
  398. Callback: func(m *Model, args []string) {
  399. m.BM.PaletteBuffer.Delete()
  400. },
  401. },
  402. Action{
  403. Bindings: []Binding{
  404. Binding{
  405. Type: BindingSingle,
  406. Single: "enter",
  407. },
  408. },
  409. Callback: func(m *Model, _ []string) {
  410. input := m.BM.PaletteBuffer.String()
  411. args := strings.Split(input, " ")
  412. if args[0] == "" {
  413. m.Mode = ModeNormal
  414. return
  415. }
  416. cmd := string([]rune(args[0])[1:])
  417. if strings.HasPrefix(args[0], "/") {
  418. for _, action := range append(m.AM.Common, append(m.AM.Normal, m.AM.Insert...)...) {
  419. if action.Callback == nil { continue }
  420. if len(action.Command) > 0 && slices.Contains(util.StandardizeBindingsArray(action.Command), util.StandardizeBinding(cmd)) {
  421. m.Mode = ModeNormal
  422. action.Callback(m, args[1:])
  423. return
  424. }
  425. }
  426. m.Mode = ModeNormal
  427. m.BM.PaletteBuffer.Clear()
  428. m.BM.PaletteBuffer.Insert("moose.error:Unknown command \"" + cmd + "\"")
  429. } else if strings.HasPrefix(args[0], "?") {
  430. m.Mode = ModeFind
  431. m.BM.PaletteBuffer.Clear()
  432. m.BM.PaletteBuffer.Insert("moose.error:Find mode unimplemented \"" + cmd + "\"")
  433. } else {
  434. m.Mode = ModeNormal
  435. m.BM.PaletteBuffer.Clear()
  436. m.BM.PaletteBuffer.Insert("moose.error:Unknown palette input \"" + cmd + "\"")
  437. }
  438. },
  439. },
  440. },
  441. }
  442. }