1
0

action.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  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/buffer"
  10. "moose/internal/util"
  11. "moose/internal/about"
  12. "moose/internal/layout"
  13. )
  14. type ActionManager = struct {
  15. Common []Action
  16. Normal []Action
  17. Insert []Action
  18. Palette []Action
  19. CM ChordManager
  20. }
  21. type Action = struct {
  22. Bindings []Binding
  23. Commands []string
  24. AskArgs bool
  25. Callback func(*Model, []string)
  26. }
  27. type Binding = struct {
  28. Type BindingType
  29. Single string
  30. Chord []string
  31. }
  32. type BindingType int
  33. const (
  34. BindingSingle BindingType = iota
  35. BindingChord
  36. )
  37. type ChordManager = struct {
  38. Recording bool
  39. Recorded []string
  40. }
  41. func NewChordManager() ChordManager {
  42. return ChordManager{
  43. Recording: false,
  44. Recorded: []string{},
  45. }
  46. }
  47. func DefaultActionManager() ActionManager {
  48. am := ActionManager{
  49. CM: NewChordManager(),
  50. Common: []Action{
  51. Action{
  52. Commands: []string{"version"},
  53. Callback: func(m *Model, args []string) {
  54. m.BM.PaletteBuffer.Clear()
  55. m.BM.PaletteBuffer.Insert("moose.info:Moose version '" + about.Version + "'")
  56. },
  57. },
  58. Action{
  59. Bindings: []Binding{
  60. Binding{
  61. Type: BindingSingle,
  62. Single: "f12",
  63. },
  64. },
  65. Callback: func(m *Model, args []string) {
  66. m.Mode = ModeNormal
  67. },
  68. },
  69. Action{
  70. Bindings: []Binding{
  71. Binding{
  72. Type: BindingSingle,
  73. Single: "ctrl+q",
  74. },
  75. },
  76. Commands: []string{"q", "quit"},
  77. AskArgs: true,
  78. Callback: func(m *Model, args []string) {
  79. m.Quit()
  80. },
  81. },
  82. Action{
  83. Bindings: []Binding{
  84. Binding{
  85. Type: BindingSingle,
  86. Single: "ctrl+s",
  87. },
  88. },
  89. Commands: []string{"s", "save"},
  90. AskArgs: false,
  91. Callback: func(m *Model, args []string) {
  92. path := ""
  93. if len(args) < 1 || args[0] == "" {
  94. if m.BM.Current().Path == "" {
  95. m.Mode = ModeNormal
  96. m.BM.PaletteBuffer.Clear()
  97. m.BM.PaletteBuffer.Insert("moose.error:Missing filename argument for save")
  98. return
  99. }
  100. }
  101. if len(args) > 0 && args[0] != "" {
  102. m.BM.Current().Path = args[0]
  103. }
  104. path = m.BM.Current().Path
  105. data := []byte(m.BM.Current().String())
  106. err := os.WriteFile(path, data, 0644)
  107. if err != nil {
  108. m.Mode = ModeNormal
  109. m.BM.PaletteBuffer.Clear()
  110. m.BM.PaletteBuffer.Insert("moose.error:Could not save to file \"" + path + "\": " + err.Error())
  111. return
  112. }
  113. m.BM.PaletteBuffer.Clear()
  114. m.BM.PaletteBuffer.Insert("moose.info:Wrote " + strconv.Itoa(len(data)) + " bytes to \"" + path + "\"")
  115. return
  116. },
  117. },
  118. Action{
  119. Bindings: []Binding{
  120. Binding{
  121. Type: BindingSingle,
  122. Single: "ctrl+shift+s",
  123. },
  124. },
  125. Commands: []string{"s", "save"},
  126. AskArgs: true,
  127. },
  128. Action{
  129. Bindings: []Binding{
  130. Binding{
  131. Type: BindingSingle,
  132. Single: "ctrl+e",
  133. },
  134. },
  135. Commands: []string{"e", "edit"},
  136. AskArgs: true,
  137. Callback: func(m *Model, args []string) {
  138. if len(args) < 1 {
  139. m.Mode = ModeNormal
  140. m.BM.PaletteBuffer.Clear()
  141. m.BM.PaletteBuffer.Insert("moose.error:Did not specify path for edit")
  142. return
  143. }
  144. content, err := os.ReadFile(args[0])
  145. if err != nil {
  146. m.Mode = ModeNormal
  147. m.BM.PaletteBuffer.Clear()
  148. m.BM.PaletteBuffer.Insert("moose.error:Could not edit file \"" + args[0] + "\": " + err.Error())
  149. return
  150. }
  151. m.BM.Current().Clear()
  152. m.BM.Current().Rope = rope.New(content)
  153. m.BM.Current().LI.Rebuild(m.BM.Current().Rope)
  154. m.BM.Current().Path = args[0]
  155. m.BM.Current().History = buffer.UndoStack{}
  156. },
  157. },
  158. Action{
  159. Bindings: []Binding{
  160. Binding{
  161. Type: BindingSingle,
  162. Single: "left",
  163. },
  164. },
  165. Callback: func(m *Model, args []string) {
  166. if m.Mode == ModePalette {
  167. m.BM.PaletteBuffer.MoveHoriz(-1)
  168. } else {
  169. m.BM.Current().MoveHoriz(-1)
  170. }
  171. },
  172. },
  173. Action{
  174. Bindings: []Binding{
  175. Binding{
  176. Type: BindingSingle,
  177. Single: "right",
  178. },
  179. },
  180. Callback: func(m *Model, args []string) {
  181. if m.Mode == ModePalette {
  182. m.BM.PaletteBuffer.MoveHoriz(1)
  183. } else {
  184. m.BM.Current().MoveHoriz(1)
  185. }
  186. },
  187. },
  188. Action{
  189. Bindings: []Binding{
  190. Binding{
  191. Type: BindingSingle,
  192. Single: "up",
  193. },
  194. },
  195. Callback: func(m *Model, args []string) {
  196. if m.Mode != ModePalette {
  197. m.BM.Current().MoveVert(-1)
  198. }
  199. },
  200. },
  201. Action{
  202. Bindings: []Binding{
  203. Binding{
  204. Type: BindingSingle,
  205. Single: "down",
  206. },
  207. },
  208. Callback: func(m *Model, args []string) {
  209. if m.Mode != ModePalette {
  210. m.BM.Current().MoveVert(1)
  211. }
  212. },
  213. },
  214. Action{
  215. Bindings: []Binding{
  216. Binding{
  217. Type: BindingSingle,
  218. Single: "shift+left",
  219. },
  220. },
  221. AskArgs: false,
  222. Callback: func(m *Model, args []string) {
  223. if m.Mode == ModePalette {
  224. m.BM.PaletteBuffer.MoveWordHoriz(-1)
  225. } else {
  226. m.BM.Current().MoveWordHoriz(-1)
  227. }
  228. },
  229. },
  230. Action{
  231. Bindings: []Binding{
  232. Binding{
  233. Type: BindingSingle,
  234. Single: "shift+right",
  235. },
  236. },
  237. AskArgs: false,
  238. Callback: func(m *Model, args []string) {
  239. if m.Mode == ModePalette {
  240. m.BM.PaletteBuffer.MoveWordHoriz(1)
  241. } else {
  242. m.BM.Current().MoveWordHoriz(1)
  243. }
  244. },
  245. },
  246. },
  247. Normal: []Action{
  248. Action{
  249. Bindings: []Binding{
  250. Binding{
  251. Type: BindingSingle,
  252. Single: "z",
  253. },
  254. },
  255. Commands: []string{"u", "undo"},
  256. AskArgs: false,
  257. Callback: func(m *Model, args []string) {
  258. if m.Mode == ModePalette {
  259. m.BM.PaletteBuffer.Undo()
  260. } else {
  261. m.BM.Current().Undo()
  262. }
  263. },
  264. },
  265. Action{
  266. Bindings: []Binding{
  267. Binding{
  268. Type: BindingSingle,
  269. Single: "y",
  270. },
  271. },
  272. Commands: []string{"r", "redo"},
  273. AskArgs: false,
  274. Callback: func(m *Model, args []string) {
  275. if m.Mode == ModePalette {
  276. m.BM.PaletteBuffer.Redo()
  277. } else {
  278. m.BM.Current().Redo()
  279. }
  280. },
  281. },
  282. Action{
  283. Bindings: []Binding{
  284. Binding{
  285. Type: BindingChord,
  286. Chord: []string{"d", "d"},
  287. },
  288. },
  289. Callback: func(m *Model, args []string) {
  290. m.BM.Current().DeleteLine()
  291. },
  292. },
  293. Action{
  294. Bindings: []Binding{
  295. Binding{
  296. Type: BindingSingle,
  297. Single: "w",
  298. },
  299. },
  300. Callback: func(m *Model, args []string) {
  301. m.Mode = ModeWrite
  302. },
  303. },
  304. Action{
  305. Bindings: []Binding{
  306. Binding{
  307. Type: BindingSingle,
  308. Single: "q",
  309. },
  310. },
  311. Callback: func(m *Model, args []string) {
  312. m.Mode = ModePalette
  313. m.BM.PaletteBuffer.Clear()
  314. m.BM.PaletteBuffer.Insert("/")
  315. },
  316. },
  317. Action{
  318. Bindings: []Binding{
  319. Binding{
  320. Type: BindingSingle,
  321. Single: "f",
  322. },
  323. },
  324. AskArgs: true,
  325. Callback: func(m *Model, args []string) {
  326. m.Mode = ModePalette
  327. m.BM.PaletteBuffer.Clear()
  328. m.BM.PaletteBuffer.Insert("?")
  329. },
  330. },
  331. Action{
  332. Bindings: []Binding{
  333. Binding{
  334. Type: BindingSingle,
  335. Single: "v",
  336. },
  337. },
  338. Commands: []string{"p", "paste"},
  339. AskArgs: false,
  340. Callback: func(m *Model, args []string) {
  341. text := clipboard.Read(clipboard.FmtText)
  342. if string(text) != "" {
  343. if m.Mode == ModePalette {
  344. m.BM.PaletteBuffer.Insert(string(text))
  345. } else {
  346. m.BM.Current().Insert(string(text))
  347. }
  348. }
  349. },
  350. },
  351. Action{
  352. Bindings: []Binding{
  353. Binding{
  354. Type: BindingSingle,
  355. Single: "enter",
  356. },
  357. },
  358. Commands: []string{"buf"},
  359. AskArgs: false,
  360. Callback: func(m *Model, args []string) {
  361. m.AddBuffer(false)
  362. },
  363. },
  364. Action{
  365. Bindings: []Binding{
  366. Binding{
  367. Type: BindingChord,
  368. Chord: []string{"l", "enter"},
  369. },
  370. },
  371. Commands: []string{"lbuf"},
  372. AskArgs: false,
  373. Callback: func(m *Model, args []string) {
  374. m.AddBuffer(true)
  375. },
  376. },
  377. Action{
  378. Bindings: []Binding{
  379. Binding{
  380. Type: BindingSingle,
  381. Single: "ctrl+left",
  382. },
  383. },
  384. Callback: func(m *Model, args []string) {
  385. m.CycleActiveBuffer(-1.0, 0.0)
  386. },
  387. },
  388. Action{
  389. Bindings: []Binding{
  390. Binding{
  391. Type: BindingSingle,
  392. Single: "ctrl+v",
  393. },
  394. },
  395. Callback: func(m *Model, args []string) {
  396. if m.LM.CurrentSplit == layout.SplitHorizontal {
  397. m.LM.CurrentSplit = layout.SplitVertical
  398. } else {
  399. m.LM.CurrentSplit = layout.SplitHorizontal
  400. }
  401. },
  402. },
  403. Action{
  404. Bindings: []Binding{
  405. Binding{
  406. Type: BindingSingle,
  407. Single: "ctrl+down",
  408. },
  409. },
  410. Callback: func(m *Model, args []string) {
  411. m.CycleActiveBuffer(0.0, 1.0)
  412. },
  413. },
  414. Action{
  415. Bindings: []Binding{
  416. Binding{
  417. Type: BindingSingle,
  418. Single: "ctrl+up",
  419. },
  420. },
  421. Callback: func(m *Model, args []string) {
  422. m.CycleActiveBuffer(0.0, -1.0)
  423. },
  424. },
  425. Action{
  426. Bindings: []Binding{
  427. Binding{
  428. Type: BindingSingle,
  429. Single: "ctrl+right",
  430. },
  431. },
  432. Callback: func(m *Model, args []string) {
  433. m.CycleActiveBuffer(1.0, 0.0)
  434. },
  435. },
  436. Action{
  437. Bindings: []Binding{
  438. Binding{
  439. Type: BindingSingle,
  440. Single: "ctrl+x",
  441. },
  442. },
  443. Callback: func(m *Model, args []string) {
  444. m.RemoveActiveBuffer()
  445. },
  446. },
  447. },
  448. Insert: []Action{
  449. Action{
  450. Bindings: []Binding{
  451. Binding{
  452. Type: BindingSingle,
  453. Single: "shift+up",
  454. },
  455. },
  456. Commands: []string{"curup"},
  457. Callback: func(m *Model, args []string) {
  458. m.BM.Current().AddCursorVert(-1)
  459. },
  460. },
  461. Action{
  462. Bindings: []Binding{
  463. Binding{
  464. Type: BindingSingle,
  465. Single: "shift+down",
  466. },
  467. },
  468. Commands: []string{"curdown"},
  469. Callback: func(m *Model, args []string) {
  470. m.BM.Current().AddCursorVert(1)
  471. },
  472. },
  473. Action{
  474. Bindings: []Binding{
  475. Binding{
  476. Type: BindingSingle,
  477. Single: "esc",
  478. },
  479. },
  480. Commands: []string{"clrc", "clearcursors"},
  481. Callback: func(m *Model, args []string) {
  482. m.BM.Current().ClearCursors()
  483. },
  484. },
  485. Action{
  486. Bindings: []Binding{
  487. Binding{
  488. Type: BindingSingle,
  489. Single: "tab",
  490. },
  491. },
  492. Callback: func(m *Model, args []string) {
  493. for _ = range 4 {
  494. m.BM.Current().Insert(" ")
  495. }
  496. },
  497. },
  498. Action{
  499. Bindings: []Binding{
  500. Binding{
  501. Type: BindingSingle,
  502. Single: "shift+tab",
  503. },
  504. },
  505. Callback: func(m *Model, args []string) {
  506. // TODO: implement m.BM.Current().remove...
  507. },
  508. },
  509. Action{
  510. Bindings: []Binding{
  511. Binding{
  512. Type: BindingSingle,
  513. Single: "backspace",
  514. },
  515. },
  516. Callback: func(m *Model, args []string) {
  517. m.BM.Current().Delete()
  518. },
  519. },
  520. Action{
  521. Bindings: []Binding{
  522. Binding{
  523. Type: BindingSingle,
  524. Single: "enter",
  525. },
  526. },
  527. Callback: func(m *Model, args []string) {
  528. m.BM.Current().Insert("\n")
  529. },
  530. },
  531. },
  532. Palette: []Action{
  533. Action{
  534. Bindings: []Binding{
  535. Binding{
  536. Type: BindingSingle,
  537. Single: "backspace",
  538. },
  539. },
  540. Callback: func(m *Model, args []string) {
  541. m.BM.PaletteBuffer.Delete()
  542. },
  543. },
  544. Action{
  545. Bindings: []Binding{
  546. Binding{
  547. Type: BindingSingle,
  548. Single: "enter",
  549. },
  550. },
  551. Callback: func(m *Model, _ []string) {
  552. input := m.BM.PaletteBuffer.String()
  553. args := strings.Split(input, " ")
  554. if args[0] == "" {
  555. m.Mode = ModeNormal
  556. return
  557. }
  558. cmd := string([]rune(args[0])[1:])
  559. if strings.HasPrefix(args[0], "/") {
  560. for _, action := range append(m.AM.Common, append(m.AM.Normal, m.AM.Insert...)...) {
  561. if action.Callback == nil { continue }
  562. if len(action.Commands) > 0 && slices.Contains(util.StandardizeBindingsArray(action.Commands), util.StandardizeBinding(cmd)) {
  563. m.Mode = ModeNormal
  564. action.Callback(m, args[1:])
  565. return
  566. }
  567. }
  568. m.Mode = ModeNormal
  569. m.BM.PaletteBuffer.Clear()
  570. m.BM.PaletteBuffer.Insert("moose.error:Unknown command \"" + cmd + "\"")
  571. } else if strings.HasPrefix(args[0], "?") {
  572. m.Mode = ModeFind
  573. m.BM.PaletteBuffer.Clear()
  574. m.BM.PaletteBuffer.Insert("moose.error:Find mode unimplemented \"" + cmd + "\"")
  575. } else {
  576. m.Mode = ModeNormal
  577. m.BM.PaletteBuffer.Clear()
  578. m.BM.PaletteBuffer.Insert("moose.error:Unknown palette input \"" + args[0] + "\"")
  579. }
  580. },
  581. },
  582. },
  583. }
  584. for i := range 9 {
  585. am.Common = append(am.Common, Action{
  586. Bindings: []Binding{
  587. Binding{
  588. Type: BindingSingle,
  589. Single: "ctrl+" + strconv.Itoa(i + 1),
  590. },
  591. },
  592. Callback: func(m *Model, args []string) {
  593. m.Mode = ModeNormal
  594. m.LM.ActiveIdx = i
  595. if bufIdx := m.LM.GetActiveBufferIdx(); bufIdx >= 0 && bufIdx < len(m.BM.Buffers) {
  596. m.BM.CurrentIdx = bufIdx
  597. } else if len(m.BM.Buffers) > 0 {
  598. m.BM.CurrentIdx = 0
  599. }
  600. },
  601. })
  602. }
  603. return am
  604. }