9fans.net/go@v0.0.7/cmd/acme/util.go (about) 1 // #include <u.h> 2 // #include <libc.h> 3 // #include <draw.h> 4 // #include <thread.h> 5 // #include <cursor.h> 6 // #include <mouse.h> 7 // #include <keyboard.h> 8 // #include <frame.h> 9 // #include <fcall.h> 10 // #include <plumb.h> 11 // #include <libsec.h> 12 // #include "dat.h" 13 // #include "fns.h" 14 15 package main 16 17 import ( 18 "fmt" 19 20 "9fans.net/go/cmd/acme/internal/bufs" 21 "9fans.net/go/cmd/acme/internal/disk" 22 "9fans.net/go/cmd/acme/internal/runes" 23 "9fans.net/go/cmd/acme/internal/ui" 24 "9fans.net/go/cmd/acme/internal/util" 25 "9fans.net/go/cmd/acme/internal/wind" 26 "9fans.net/go/cmd/internal/base" 27 ) 28 29 func errorwin1(dir []rune, incl [][]rune) *wind.Window { 30 var r []rune 31 if len(dir) > 0 { 32 r = append(r, dir...) 33 r = append(r, '/') 34 } 35 r = append(r, []rune("+Errors")...) 36 w := ui.LookFile(r) 37 if w == nil { 38 if len(wind.TheRow.Col) == 0 { 39 if wind.RowAdd(&wind.TheRow, nil, -1) == nil { 40 util.Fatal("can't create column to make error window") 41 } 42 } 43 w = ui.ColaddAndMouse(wind.TheRow.Col[len(wind.TheRow.Col)-1], nil, nil, -1) 44 w.Filemenu = false 45 wind.Winsetname(w, r) 46 xfidlog(w, "new") 47 } 48 for i := len(incl) - 1; i >= 0; i-- { 49 wind.Winaddincl(w, runes.Clone(incl[i])) 50 } 51 w.Autoindent = wind.GlobalAutoindent 52 return w 53 } 54 55 // make new window, if necessary; return with it locked 56 func errorwin(md *base.Mntdir, owner rune) *wind.Window { 57 var w *wind.Window 58 for { 59 if md == nil { 60 w = errorwin1(nil, nil) 61 } else { 62 w = errorwin1(md.Dir, md.Incl) 63 } 64 wind.Winlock(w, owner) 65 if w.Col != nil { 66 break 67 } 68 // window was deleted too fast 69 wind.Winunlock(w) 70 } 71 return w 72 } 73 74 /* 75 * Incoming window should be locked. 76 * It will be unlocked and returned window 77 * will be locked in its place. 78 */ 79 func errorwinforwin(w *wind.Window) *wind.Window { 80 t := &w.Body 81 dir := wind.Dirname(t, nil) 82 if len(dir) == 1 && dir[0] == '.' { // sigh 83 dir = nil 84 } 85 incl := make([][]rune, len(w.Incl)) 86 for i := range w.Incl { 87 incl[i] = runes.Clone(w.Incl[i]) 88 } 89 owner := w.Owner 90 wind.Winunlock(w) 91 for { 92 w = errorwin1(dir, incl) 93 wind.Winlock(w, owner) 94 if w.Col != nil { 95 break 96 } 97 // window deleted too fast 98 wind.Winunlock(w) 99 } 100 return w 101 } 102 103 type Warning struct { 104 md *base.Mntdir 105 buf disk.Buffer 106 next *Warning 107 } 108 109 var warnings *Warning 110 111 func addwarningtext(md *base.Mntdir, r []rune) { 112 for warn := warnings; warn != nil; warn = warn.next { 113 if warn.md == md { 114 warn.buf.Insert(warn.buf.Len(), r) 115 return 116 } 117 } 118 warn := new(Warning) 119 warn.next = warnings 120 warn.md = md 121 if md != nil { 122 fsysincid(md) 123 } 124 warnings = warn 125 warn.buf.Insert(0, r) 126 select { 127 case cwarn <- 0: 128 default: 129 } 130 } 131 132 // called while row is locked 133 func flushwarnings() { 134 var next *Warning 135 for warn := warnings; warn != nil; warn = next { 136 w := errorwin(warn.md, 'E') 137 t := &w.Body 138 owner := w.Owner 139 if owner == 0 { 140 w.Owner = 'E' 141 } 142 wind.Wincommit(w, t) 143 /* 144 * Most commands don't generate much output. For instance, 145 * Edit ,>cat goes through /dev/cons and is already in blocks 146 * because of the i/o system, but a few can. Edit ,p will 147 * put the entire result into a single hunk. So it's worth doing 148 * this in blocks (and putting the text in a buffer in the first 149 * place), to avoid a big memory footprint. 150 */ 151 r := bufs.AllocRunes() 152 q0 := t.Len() 153 var nr int 154 for n := 0; n < warn.buf.Len(); n += nr { 155 nr = warn.buf.Len() - n 156 if nr > bufs.RuneLen { 157 nr = bufs.RuneLen 158 } 159 warn.buf.Read(n, r[:nr]) 160 wind.Textbsinsert(t, t.Len(), r[:nr], true, &nr) 161 } 162 wind.Textshow(t, q0, t.Len(), true) 163 wind.Winsettag(t.W) 164 wind.Textscrdraw(t) 165 w.Owner = owner 166 w.Dirty = false 167 wind.Winunlock(w) 168 warn.buf.Close() 169 next = warn.next 170 if warn.md != nil { 171 fsysdelid(warn.md) 172 } 173 } 174 warnings = nil 175 } 176 177 func warning(md *base.Mntdir, format string, args ...interface{}) { 178 addwarningtext(md, []rune(fmt.Sprintf(format, args...))) 179 } 180 181 func rgetc(v interface{}, n int) rune { 182 r := v.([]rune) 183 if n >= len(r) { 184 return 0 185 } 186 return r[n] 187 }