1
0

action.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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. },
  351. Insert: []Action{
  352. Action{
  353. Bindings: []Binding{
  354. Binding{
  355. Type: BindingSingle,
  356. Single: "shift+up",
  357. },
  358. },
  359. Commands: []string{"curup"},
  360. Callback: func(m *Model, args []string) {
  361. m.BM.Current().AddCursorVert(-1)
  362. },
  363. },
  364. Action{
  365. Bindings: []Binding{
  366. Binding{
  367. Type: BindingSingle,
  368. Single: "shift+down",
  369. },
  370. },
  371. Commands: []string{"curdown"},
  372. Callback: func(m *Model, args []string) {
  373. m.BM.Current().AddCursorVert(1)
  374. },
  375. },
  376. Action{
  377. Bindings: []Binding{
  378. Binding{
  379. Type: BindingSingle,
  380. Single: "esc",
  381. },
  382. },
  383. Commands: []string{"clrc", "clearcursors"},
  384. Callback: func(m *Model, args []string) {
  385. m.BM.Current().ClearCursors()
  386. },
  387. },
  388. Action{
  389. Bindings: []Binding{
  390. Binding{
  391. Type: BindingSingle,
  392. Single: "tab",
  393. },
  394. },
  395. Callback: func(m *Model, args []string) {
  396. for _ = range 4 {
  397. m.BM.Current().Insert(" ")
  398. }
  399. },
  400. },
  401. Action{
  402. Bindings: []Binding{
  403. Binding{
  404. Type: BindingSingle,
  405. Single: "shift+tab",
  406. },
  407. },
  408. Callback: func(m *Model, args []string) {
  409. // TODO: implement m.BM.Current().remove...
  410. },
  411. },
  412. Action{
  413. Bindings: []Binding{
  414. Binding{
  415. Type: BindingSingle,
  416. Single: "backspace",
  417. },
  418. },
  419. Callback: func(m *Model, args []string) {
  420. m.BM.Current().Delete()
  421. },
  422. },
  423. Action{
  424. Bindings: []Binding{
  425. Binding{
  426. Type: BindingSingle,
  427. Single: "enter",
  428. },
  429. },
  430. Callback: func(m *Model, args []string) {
  431. m.BM.Current().Insert("\n")
  432. },
  433. },
  434. },
  435. Palette: []Action{
  436. Action{
  437. Bindings: []Binding{
  438. Binding{
  439. Type: BindingSingle,
  440. Single: "backspace",
  441. },
  442. },
  443. Callback: func(m *Model, args []string) {
  444. m.BM.PaletteBuffer.Delete()
  445. },
  446. },
  447. Action{
  448. Bindings: []Binding{
  449. Binding{
  450. Type: BindingSingle,
  451. Single: "enter",
  452. },
  453. },
  454. Callback: func(m *Model, _ []string) {
  455. input := m.BM.PaletteBuffer.String()
  456. args := strings.Split(input, " ")
  457. if args[0] == "" {
  458. m.Mode = ModeNormal
  459. return
  460. }
  461. cmd := string([]rune(args[0])[1:])
  462. if strings.HasPrefix(args[0], "/") {
  463. for _, action := range append(m.AM.Common, append(m.AM.Normal, m.AM.Insert...)...) {
  464. if action.Callback == nil { continue }
  465. if len(action.Commands) > 0 && slices.Contains(util.StandardizeBindingsArray(action.Commands), util.StandardizeBinding(cmd)) {
  466. m.Mode = ModeNormal
  467. action.Callback(m, args[1:])
  468. return
  469. }
  470. }
  471. m.Mode = ModeNormal
  472. m.BM.PaletteBuffer.Clear()
  473. m.BM.PaletteBuffer.Insert("moose.error:Unknown command \"" + cmd + "\"")
  474. } else if strings.HasPrefix(args[0], "?") {
  475. m.Mode = ModeFind
  476. m.BM.PaletteBuffer.Clear()
  477. m.BM.PaletteBuffer.Insert("moose.error:Find mode unimplemented \"" + cmd + "\"")
  478. } else {
  479. m.Mode = ModeNormal
  480. m.BM.PaletteBuffer.Clear()
  481. m.BM.PaletteBuffer.Insert("moose.error:Unknown palette input \"" + args[0] + "\"")
  482. }
  483. },
  484. },
  485. },
  486. }
  487. }