1
0

action.go 10 KB

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