action.go 10 KB

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