action.go 11 KB

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