PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/goacme.go

https://bitbucket.org/santucco/goacme
Go | 1306 lines | 431 code | 590 blank | 285 comment | 73 complexity | fb05f975d9ceafadf81ca791194ba300 MD5 | raw file
  1. /*2:*/
  2. //line goacme.w:49
  3. // Copyright (c) 2013, 2014 Alexander Sychev. All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * The name of author may not be used to endorse or promote products derived from
  16. // this software without specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. /*:2*/
  30. /*3:*/
  31. //line goacme.w:78
  32. // Package goacme provides interface to acme programming environment
  33. package goacme
  34. import(
  35. /*6:*/
  36. //line goacme.w:135
  37. "code.google.com/p/goplan9/plan9"
  38. "code.google.com/p/goplan9/plan9/client"
  39. "sync"
  40. "fmt"
  41. /*:6*/
  42. /*20:*/
  43. //line goacme.w:300
  44. "io"
  45. /*:20*/
  46. /*41:*/
  47. //line goacme.w:513
  48. "errors"
  49. /*:41*/
  50. /*53:*/
  51. //line goacme.w:641
  52. "strings"
  53. /*:53*/
  54. //line goacme.w:83
  55. )
  56. type(
  57. /*5:*/
  58. //line goacme.w:127
  59. // Window is a structure to manipulate a particular acme's window.
  60. Window struct{
  61. id int
  62. /*21:*/
  63. //line goacme.w:304
  64. files map[string]*client.Fid
  65. /*:21*/
  66. /*29:*/
  67. //line goacme.w:379
  68. prev*Window
  69. next*Window
  70. /*:29*/
  71. /*55:*/
  72. //line goacme.w:660
  73. ch chan*Event
  74. /*:55*/
  75. //line goacme.w:131
  76. }
  77. /*:5*/
  78. /*35:*/
  79. //line goacme.w:444
  80. Event struct{
  81. /*40:*/
  82. //line goacme.w:508
  83. // Origin will be an origin of action with type ActionOrigin
  84. Origin ActionOrigin
  85. /*:40*/
  86. /*46:*/
  87. //line goacme.w:550
  88. // Type will be an type of action with type ActionType
  89. Type ActionType
  90. /*:46*/
  91. /*49:*/
  92. //line goacme.w:577
  93. begin int
  94. // Begin is a start address of a text of the action
  95. Begin int
  96. end int
  97. // End is an end address of the text of the action
  98. End int
  99. /*:49*/
  100. /*51:*/
  101. //line goacme.w:594
  102. flag int
  103. // IsBuiltin is a flag the action is recognised like an \.{Acme}'s builtin
  104. IsBuiltin bool
  105. // NoLoad is a flag of acme can interpret the action without loading a new file
  106. NoLoad bool
  107. // IsFile is a flag the Text is a file or window name
  108. IsFile bool
  109. // Text is a text arguments of the action, perhaps with address
  110. Text string
  111. // Arg is a text of chorded argument if any
  112. Arg string
  113. /*:51*/
  114. //line goacme.w:446
  115. }
  116. /*:35*/
  117. /*38:*/
  118. //line goacme.w:490
  119. // ActionOrigin is a origin of the action
  120. ActionOrigin int
  121. /*:38*/
  122. /*44:*/
  123. //line goacme.w:532
  124. // ActionType is a type of the action
  125. ActionType int
  126. /*:44*/
  127. /*69:*/
  128. //line goacme.w:981
  129. wrapper struct{
  130. f io.ReadWriteSeeker
  131. }
  132. /*:69*/
  133. //line goacme.w:87
  134. )
  135. var(
  136. /*7:*/
  137. //line goacme.w:142
  138. fsys*client.Fsys
  139. once sync.Once
  140. /*:7*/
  141. /*28:*/
  142. //line goacme.w:374
  143. fwin*Window
  144. lwin*Window
  145. /*:28*/
  146. /*42:*/
  147. //line goacme.w:517
  148. // ErrInvalidOrigin will be returned if a case of an unexpected origin of action
  149. ErrInvalidOrigin= errors.New("invalid origin of action")
  150. /*:42*/
  151. /*47:*/
  152. //line goacme.w:555
  153. // ErrInvalidType will be returned if a case of an unexpected type of action
  154. ErrInvalidType= errors.New("invalid type of action")
  155. /*:47*/
  156. /*58:*/
  157. //line goacme.w:729
  158. // ErrChannelAlreadyOpened will be returned
  159. // if channel of events is opened by call of EventChannel
  160. ErrChannelAlreadyOpened= errors.New("channel of events is already opened")
  161. /*:58*/
  162. //line goacme.w:91
  163. )
  164. /*39:*/
  165. //line goacme.w:495
  166. const(
  167. // Edit is the origin for writes to the body or tag file
  168. Edit ActionOrigin= 1<<iota
  169. // File is the origin for through the window's other files
  170. File
  171. // Keyboard is the origin for keyboard actions
  172. Keyboard
  173. // Mouse is the origin for mouse actions
  174. Mouse
  175. )
  176. /*:39*/
  177. /*45:*/
  178. //line goacme.w:537
  179. const(
  180. Delete ActionType= 1<<iota
  181. Insert
  182. Look
  183. Execute
  184. // Tag is a flag points out the event has occured in the tag of the window
  185. Tag
  186. // TagMask is a mask points out the event should be masked by tag
  187. TagMask
  188. )
  189. /*:45*/
  190. //line goacme.w:94
  191. /*:3*/
  192. /*9:*/
  193. //line goacme.w:157
  194. // New creates a new window and returns *Window or error
  195. func New()(*Window,error){
  196. /*8:*/
  197. //line goacme.w:147
  198. {
  199. var err error
  200. once.Do(func(){fsys,err= client.MountService("acme")})
  201. if err!=nil{
  202. return nil,err
  203. }
  204. }
  205. /*:8*/
  206. //line goacme.w:160
  207. f,err:=fsys.Open("new/ctl",plan9.OREAD)
  208. if err!=nil{
  209. return nil,err
  210. }
  211. defer f.Close()
  212. var id int
  213. if _,err:=fmt.Fscan(f,&id);err!=nil{
  214. return nil,err
  215. }
  216. return Open(id)
  217. }
  218. /*:9*/
  219. /*10:*/
  220. //line goacme.w:174
  221. // Open opens a window with a specified id and returns *Window or error
  222. func Open(id int)(*Window,error){
  223. /*8:*/
  224. //line goacme.w:147
  225. {
  226. var err error
  227. once.Do(func(){fsys,err= client.MountService("acme")})
  228. if err!=nil{
  229. return nil,err
  230. }
  231. }
  232. /*:8*/
  233. //line goacme.w:177
  234. if err:=fsys.Access(fmt.Sprintf("%d",id),plan9.OREAD);err!=nil{
  235. return nil,err
  236. }
  237. this:=&Window{id:id}
  238. /*22:*/
  239. //line goacme.w:308
  240. this.files= make(map[string]*client.Fid)
  241. /*:22*/
  242. /*30:*/
  243. //line goacme.w:384
  244. this.prev= lwin
  245. this.next= nil
  246. if fwin==nil{
  247. fwin= this
  248. }
  249. if lwin!=nil{
  250. lwin.next= this
  251. }
  252. lwin= this
  253. /*:30*/
  254. /*64:*/
  255. //line goacme.w:868
  256. if _,err:=this.File("addr");err!=nil{
  257. return nil,err
  258. }
  259. /*:64*/
  260. //line goacme.w:182
  261. return this,nil
  262. }
  263. /*:10*/
  264. /*11:*/
  265. //line goacme.w:187
  266. // Close releases all resources of the window
  267. func(this*Window)Close()error{
  268. /*23:*/
  269. //line goacme.w:312
  270. for _,v:=range this.files{
  271. v.Close()
  272. }
  273. /*:23*/
  274. /*31:*/
  275. //line goacme.w:396
  276. if this.next!=nil{
  277. this.next.prev= this.prev
  278. }
  279. if this.prev!=nil{
  280. this.prev.next= this.next
  281. }
  282. if fwin==this{
  283. fwin= this.next
  284. }
  285. if lwin==this{
  286. lwin= this.prev
  287. }
  288. /*:31*/
  289. //line goacme.w:190
  290. return nil
  291. }
  292. /*:11*/
  293. /*15:*/
  294. //line goacme.w:224
  295. // Read reads len(p) bytes from "body" file of the window.
  296. // Read returns a count of read bytes or error.
  297. func(this*Window)Read(p[]byte)(int,error){
  298. f,err:=this.File("body")
  299. if err!=nil{
  300. return 0,err
  301. }
  302. return f.Read(p)
  303. }
  304. /*:15*/
  305. /*16:*/
  306. //line goacme.w:236
  307. // Write writes len(p) bytes to "body" file of the window.
  308. // Write returns a count of written bytes or error.
  309. func(this*Window)Write(p[]byte)(int,error){
  310. f,err:=this.File("body")
  311. if err!=nil{
  312. return 0,err
  313. }
  314. /*71:*/
  315. //line goacme.w:1016
  316. f= &wrapper{f:f}
  317. /*:71*/
  318. //line goacme.w:244
  319. return f.Write(p)
  320. }
  321. /*:16*/
  322. /*19:*/
  323. //line goacme.w:284
  324. // Seek sets a position for the next Read or Write to offset, interpreted
  325. // according to whence: 0 means relative to the origin of the file, 1 means
  326. // relative to the current offset, and 2 means relative to the end.
  327. // Seek returns the new offset or error
  328. func(this*Window)Seek(offset int64,whence int)(ret int64,err error){
  329. f,err:=this.File("body")
  330. if err!=nil{
  331. return 0,err
  332. }
  333. return f.Seek(offset,whence)
  334. }
  335. /*:19*/
  336. /*24:*/
  337. //line goacme.w:318
  338. // File returns io.ReadWriteSeeker of corresponding file of the windows or error
  339. func(this*Window)File(file string)(io.ReadWriteSeeker,error){
  340. fid,ok:=this.files[file]
  341. if!ok{
  342. var err error
  343. if fid,err= fsys.Open(fmt.Sprintf("%d/%s",this.id,file),plan9.ORDWR);err!=nil{
  344. if fid,err= fsys.Open(fmt.Sprintf("%d/%s",this.id,file),plan9.OREAD);err!=nil{
  345. if fid,err= fsys.Open(fmt.Sprintf("%d/%s",this.id,file),plan9.OWRITE);err!=nil{
  346. return nil,err
  347. }
  348. }
  349. }
  350. this.files[file]= fid
  351. }
  352. var f io.ReadWriteSeeker= fid
  353. /*71:*/
  354. //line goacme.w:1016
  355. f= &wrapper{f:f}
  356. /*:71*/
  357. //line goacme.w:334
  358. return f,nil
  359. }
  360. /*:24*/
  361. /*25:*/
  362. //line goacme.w:339
  363. // Del deletes the window, without a prompt if sure is true.
  364. func(this*Window)Del(sure bool)error{
  365. f,err:=this.File("ctl")
  366. if err!=nil{
  367. return err
  368. }
  369. s:="del"
  370. if sure{
  371. s= "delete"
  372. }
  373. _,err= f.Write([]byte(s))
  374. return err
  375. }
  376. /*:25*/
  377. /*32:*/
  378. //line goacme.w:412
  379. // DeleteAll deletes all the windows opened in a session
  380. func DeleteAll(){
  381. for fwin!=nil{
  382. fwin.Del(true)
  383. fwin.Close()
  384. }
  385. }
  386. /*:32*/
  387. /*36:*/
  388. //line goacme.w:452
  389. func readFields(r io.Reader)(o rune,t rune,b int,e int,f int,s string,err error){
  390. var l int
  391. if _,err= fmt.Fscanf(r,"%c%c%d %d %d %d",&o,&t,&b,&e,&f,&l);err!=nil{
  392. return
  393. }
  394. if l!=0{
  395. rs:=make([]rune,l)
  396. for i:=0;i<l;i++{
  397. if _,err= fmt.Fscanf(r,"%c",&rs[i]);err!=nil{
  398. return
  399. }
  400. }
  401. s= string(rs)
  402. }
  403. var nl[1]byte
  404. if _,err= r.Read(nl[:]);err!=nil{
  405. return
  406. }
  407. return
  408. }
  409. /*:36*/
  410. /*37:*/
  411. //line goacme.w:475
  412. func readEvent(r io.Reader)(*Event,error){
  413. o,t,b,e,f,s,err:=readFields(r)
  414. if err!=nil{
  415. return nil,err
  416. }
  417. var ev Event
  418. /*43:*/
  419. //line goacme.w:522
  420. switch o{
  421. case'E':ev.Origin= Edit
  422. case'F':ev.Origin= File
  423. case'K':ev.Origin= Keyboard
  424. case'M':ev.Origin= Mouse
  425. default:return nil,ErrInvalidOrigin
  426. }
  427. /*:43*/
  428. //line goacme.w:482
  429. /*48:*/
  430. //line goacme.w:560
  431. switch t{
  432. case'D':ev.Type= Delete
  433. case'd':ev.Type= Delete|Tag
  434. case'I':ev.Type= Insert
  435. case'i':ev.Type= Insert|Tag
  436. case'L':ev.Type= Look
  437. case'l':ev.Type= Look|Tag
  438. case'X':ev.Type= Execute
  439. case'x':ev.Type= Execute|Tag
  440. default:return nil,ErrInvalidType
  441. }
  442. /*:48*/
  443. //line goacme.w:483
  444. /*50:*/
  445. //line goacme.w:586
  446. ev.begin= b
  447. ev.Begin= b
  448. ev.end= e
  449. ev.End= e
  450. /*:50*/
  451. //line goacme.w:484
  452. /*52:*/
  453. //line goacme.w:608
  454. ev.flag= f
  455. if ev.Type&Execute==Execute{
  456. ev.IsBuiltin= (ev.flag&1)==1
  457. }else if ev.Type&Look==Look{
  458. ev.NoLoad= (ev.flag&1)==1
  459. ev.IsFile= (ev.flag&4)==4
  460. }
  461. ev.Text= s
  462. // if there is an expansion
  463. if(ev.flag&2)==2{
  464. _,_,ev.Begin,ev.End,_,ev.Text,err= readFields(r)
  465. if err!=nil{
  466. return nil,err
  467. }
  468. }
  469. // if there is a chording
  470. if(ev.flag&8)==8{
  471. _,_,_,_,_,ev.Arg,err= readFields(r)
  472. if err!=nil{
  473. return nil,err
  474. }
  475. _,_,_,_,_,_,err= readFields(r)
  476. if err!=nil{
  477. return nil,err
  478. }
  479. }
  480. /*54:*/
  481. //line goacme.w:645
  482. if len(ev.Text)> 0{
  483. f:=strings.Fields(ev.Text)
  484. if len(f)> 1{
  485. ev.Text= f[0]
  486. s:=ev.Arg
  487. if len(s)> 0{
  488. s= " "+ev.Arg
  489. }
  490. ev.Arg= strings.Join(f[1:]," ")+s
  491. }
  492. }
  493. /*:54*/
  494. //line goacme.w:638
  495. /*:52*/
  496. //line goacme.w:485
  497. return&ev,nil
  498. }
  499. /*:37*/
  500. /*56:*/
  501. //line goacme.w:664
  502. // EventChannel returns a channel of *Event with a buffer size
  503. // from which events can be read or error.
  504. // Only ActionTypes set in tmask are used.
  505. // If TagMask is set in tmask, the event will be masked by tag. Otherwise Tag flag will be ignored.
  506. // First call of EventChannel starts a goroutine to read events from "event" file
  507. // and put them to the channel. Subsequent calls of EventChannel will return the same channel.
  508. func(this*Window)EventChannel(size int,tmask ActionType)(<-chan*Event,error){
  509. if this.ch!=nil{
  510. return this.ch,nil
  511. }
  512. /*57:*/
  513. //line goacme.w:704
  514. old:=false
  515. {
  516. var em string
  517. if tmask&Delete==Delete{
  518. em+= "D"
  519. }
  520. if tmask&Insert==Insert{
  521. em+= "I"
  522. }
  523. if tmask&Look==Look{
  524. em+= "L"
  525. }
  526. if tmask&Execute==Execute{
  527. em+= "X"
  528. }
  529. if tmask&TagMask!=TagMask{
  530. em+= strings.ToLower(em)
  531. }
  532. if err:=this.WriteCtl("events %s\n",em);err!=nil{
  533. old= true
  534. }
  535. }
  536. /*:57*/
  537. //line goacme.w:675
  538. f,err:=this.File("event")
  539. if err!=nil{
  540. return nil,err
  541. }
  542. if tmask&TagMask!=TagMask{
  543. tmask|= Tag
  544. }
  545. this.ch= make(chan*Event,size)
  546. go func(){
  547. for ev,err:=readEvent(f);err==nil;ev,err= readEvent(f){
  548. if old&&ev.Type&tmask!=ev.Type{
  549. if ev.Type&Insert!=Insert&&ev.Type&Delete!=Delete{
  550. this.UnreadEvent(ev)
  551. }
  552. continue
  553. }
  554. this.ch<-ev
  555. }
  556. close(this.ch)
  557. this.ch= nil
  558. }()
  559. return this.ch,nil
  560. }
  561. /*:56*/
  562. /*59:*/
  563. //line goacme.w:735
  564. // reads an event from "event" file of the window and returns *Event or error
  565. func(this*Window)ReadEvent()(*Event,error){
  566. if this.ch!=nil{
  567. return nil,ErrChannelAlreadyOpened
  568. }
  569. f,err:=this.File("event")
  570. if err!=nil{
  571. return nil,err
  572. }
  573. return readEvent(f)
  574. }
  575. /*:59*/
  576. /*60:*/
  577. //line goacme.w:750
  578. // UnreadEvent writes event ev back to the "event" file,
  579. // indicating to acme that it should be handled internally.
  580. func(this*Window)UnreadEvent(ev*Event)error{
  581. f,err:=this.File("event")
  582. if err!=nil{
  583. return err
  584. }
  585. var o rune
  586. switch ev.Origin{
  587. case Edit:o= 'E'
  588. case File:o= 'F'
  589. case Keyboard:o= 'K'
  590. case Mouse:o= 'M'
  591. default:return ErrInvalidOrigin
  592. }
  593. var t rune
  594. switch ev.Type{
  595. case Delete:t= 'D'
  596. case Delete|Tag:t= 'd'
  597. case Insert:t= 'I'
  598. case Insert|Tag:t= 'i'
  599. case Look:t= 'L'
  600. case Look|Tag:t= 'l'
  601. case Execute:t= 'X'
  602. case Execute|Tag:t= 'x'
  603. default:return ErrInvalidType
  604. }
  605. _,err= fmt.Fprintf(f,"%c%c%d %d\n",o,t,ev.begin,ev.end)
  606. return err
  607. }
  608. /*:60*/
  609. /*62:*/
  610. //line goacme.w:837
  611. // WriteAddr writes format with args in "addr" file of the window
  612. func(this*Window)WriteAddr(format string,args...interface{})error{
  613. f,err:=this.File("addr")
  614. if err!=nil{
  615. return err
  616. }
  617. if len(args)> 0{
  618. format= fmt.Sprintf(format,args...)
  619. }
  620. _,err= f.Write([]byte(format))
  621. return err
  622. }
  623. /*:62*/
  624. /*63:*/
  625. //line goacme.w:852
  626. // ReadAddr reads the address of the next read/write operation from "addr" file of the window.
  627. // ReadAddr return begin and end offsets in symbols or error
  628. func(this*Window)ReadAddr()(begin int,end int,err error){
  629. f,err:=this.File("addr")
  630. if err!=nil{
  631. return
  632. }
  633. if _,err= f.Seek(0,0);err!=nil{
  634. return
  635. }
  636. _,err= fmt.Fscanf(f,"%d %d",&begin,&end)
  637. return
  638. }
  639. /*:63*/
  640. /*66:*/
  641. //line goacme.w:902
  642. // WriteCtl writes format with args in "ctl" file of the window
  643. // In case format is not ended by newline, '\n' will be added to the end of format
  644. func(this*Window)WriteCtl(format string,args...interface{})error{
  645. f,err:=this.File("ctl")
  646. if err!=nil{
  647. return err
  648. }
  649. if len(args)> 0{
  650. format= fmt.Sprintf(format,args...)
  651. }
  652. if len(format)>=0&&format[len(format)-1]!='\n'{
  653. format+= "\n"
  654. }
  655. if _,err= f.Seek(0,0);err!=nil{
  656. return err
  657. }
  658. _,err= f.Write([]byte(format))
  659. return err
  660. }
  661. /*:66*/
  662. /*67:*/
  663. //line goacme.w:925
  664. // ReadCtl reads the address of the next read/write operation from "ctl" file of the window.
  665. // ReadCtl returns:
  666. // id - the window ID
  667. // tlen - number of characters (runes) in the tag;
  668. // blen - number of characters in the body;
  669. // isdir - true if the window is a directory, false otherwise;
  670. // isdirty - true if the window is modified, falseotherwise;
  671. // wwidth - the width of the window in pixels;
  672. // font - the name of the font used in the window;
  673. // twidth - the width of a tab character in pixels;
  674. // error - in case of any error.
  675. func(this*Window)ReadCtl()(id int,tlen int,blen int,isdir bool,isdirty bool,wwidth int,font string,twidth int,err error){
  676. f,err:=this.File("ctl")
  677. if err!=nil{
  678. return
  679. }
  680. if _,err= f.Seek(0,0);err!=nil{
  681. return
  682. }
  683. var dir,dirty int
  684. _,err= fmt.Fscanf(f,"%d %d %d %d %d %d %s %d",&id,&tlen,&blen,&dir,&dirty,&wwidth,&font,&twidth)
  685. isdir= dir==1
  686. isdirty= dirty==1
  687. return
  688. }
  689. /*:67*/
  690. /*70:*/
  691. //line goacme.w:987
  692. func(this*wrapper)Read(p[]byte)(int,error){
  693. return this.f.Read(p)
  694. }
  695. func(this*wrapper)Write(p[]byte)(int,error){
  696. if len(p)<8168{
  697. return this.f.Write(p)
  698. }
  699. c:=0
  700. for i:=0;i<len(p);i+= 8168{
  701. n:=i+8168
  702. if n> len(p){
  703. n= len(p)
  704. }
  705. n,e:=this.f.Write(p[i:n])
  706. c+= n
  707. if e!=nil{
  708. return c,e
  709. }
  710. }
  711. return c,nil
  712. }
  713. func(this*wrapper)Seek(offset int64,whence int)(ret int64,err error){
  714. return this.f.Seek(offset,whence)
  715. }
  716. /*:70*/