9fans.net/go@v0.0.7/cmd/acme/internal/fileload/text1.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 <complete.h> 13 // #include "dat.h" 14 // #include "fns.h" 15 16 package fileload 17 18 import ( 19 "crypto/sha1" 20 "hash" 21 "io" 22 "os" 23 "sort" 24 25 "9fans.net/go/cmd/acme/internal/alog" 26 "9fans.net/go/cmd/acme/internal/bufs" 27 "9fans.net/go/cmd/acme/internal/complete" 28 "9fans.net/go/cmd/acme/internal/runes" 29 "9fans.net/go/cmd/acme/internal/util" 30 "9fans.net/go/cmd/acme/internal/wind" 31 ) 32 33 var Ismtpt = func(string) bool { return false } 34 35 func Textload(t *wind.Text, q0 int, file string, setqid bool) int { 36 if len(t.Cache) > 0 || t.Len() != 0 || t.W == nil || t != &t.W.Body { 37 util.Fatal("text.load") 38 } 39 if t.W.IsDir && len(t.File.Name()) == 0 { 40 alog.Printf("empty directory name") 41 return -1 42 } 43 if Ismtpt(file) { 44 alog.Printf("will not open self mount point %s\n", file) 45 return -1 46 } 47 f, err := os.Open(file) 48 if err != nil { 49 alog.Printf("can't open %s: %v\n", file, err) 50 return -1 51 } 52 defer f.Close() 53 info, err := f.Stat() 54 if err != nil { 55 alog.Printf("can't fstat %s: %v\n", file, err) 56 return -1 57 } 58 nulls := false 59 var h hash.Hash 60 var rp []rune 61 var i int 62 var n int 63 var q1 int 64 if info.IsDir() { 65 // this is checked in get() but it's possible the file changed underfoot 66 if len(t.File.Text) > 1 { 67 alog.Printf("%s is a directory; can't read with multiple windows on it\n", file) 68 return -1 69 } 70 t.W.IsDir = true 71 t.W.Filemenu = false 72 if len(t.File.Name()) > 0 && t.File.Name()[len(t.File.Name())-1] != '/' { 73 rp := make([]rune, len(t.File.Name())+1) 74 copy(rp, t.File.Name()) 75 rp[len(t.File.Name())] = '/' 76 wind.Winsetname(t.W, rp) 77 } 78 var dlp []*wind.Dirlist 79 for { 80 // TODO(rsc): sort order here should be before /, not after 81 // Can let ReadDir(-1) do it. 82 dirs, err := f.ReadDir(100) 83 for _, dir := range dirs { 84 dl := new(wind.Dirlist) 85 name := dir.Name() 86 if dir.IsDir() { 87 name += "/" 88 } 89 dl.R = []rune(name) 90 dl.Wid = t.Fr.Font.StringWidth(name) 91 dlp = append(dlp, dl) 92 } 93 if err != nil { 94 break 95 } 96 } 97 sort.Slice(dlp, func(i, j int) bool { 98 return runes.Compare(dlp[i].R, dlp[j].R) < 0 99 }) 100 t.W.Dlp = dlp 101 wind.Textcolumnate(t, dlp) 102 q1 = t.Len() 103 } else { 104 t.W.IsDir = false 105 t.W.Filemenu = true 106 if q0 == 0 { 107 h = sha1.New() 108 } 109 q1 = q0 + fileload(t.File, q0, f, &nulls, h) 110 } 111 if setqid { 112 if h != nil { 113 h.Sum(t.File.SHA1[:0]) 114 h = nil 115 } else { 116 t.File.SHA1 = [20]byte{} 117 } 118 t.File.Info = info 119 } 120 f.Close() 121 rp = bufs.AllocRunes() 122 for q := q0; q < q1; q += n { 123 n = q1 - q 124 if n > bufs.RuneLen { 125 n = bufs.RuneLen 126 } 127 t.File.Read(q, rp[:n]) 128 if q < t.Org { 129 t.Org += n 130 } else if q <= t.Org+t.Fr.NumChars { 131 t.Fr.Insert(rp[:n], q-t.Org) 132 } 133 if t.Fr.LastLineFull { 134 break 135 } 136 } 137 bufs.FreeRunes(rp) 138 for i = 0; i < len(t.File.Text); i++ { 139 u := t.File.Text[i] 140 if u != t { 141 if u.Org > u.Len() { // will be 0 because of reset(), but safety first 142 u.Org = 0 143 } 144 wind.Textresize(u, u.All, true) 145 wind.Textbacknl(u, u.Org, 0) // go to beginning of line 146 } 147 wind.Textsetselect(u, q0, q0) 148 } 149 if nulls { 150 alog.Printf("%s: NUL bytes elided\n", file) 151 } 152 return q1 - q0 153 } 154 155 func Textcomplete(t *wind.Text) []rune { 156 // control-f: filename completion; works back to white space or / 157 if t.Q0 < t.Len() && t.RuneAt(t.Q0) > ' ' { // must be at end of word 158 return nil 159 } 160 nstr := wind.Textfilewidth(t, t.Q0, true) 161 str := make([]rune, 0, nstr) 162 npath := wind.Textfilewidth(t, t.Q0-nstr, false) 163 path_ := make([]rune, 0, npath) 164 165 for q := t.Q0 - nstr; q < t.Q0; q++ { 166 str = append(str, t.RuneAt(q)) 167 } 168 for q := t.Q0 - nstr - npath; q < t.Q0-nstr; q++ { 169 path_ = append(path_, t.RuneAt(q)) 170 } 171 var dir []rune 172 // is path rooted? if not, we need to make it relative to window path 173 if npath > 0 && path_[0] == '/' { 174 dir = path_ 175 } else { 176 dir = wind.Dirname(t, nil) 177 if len(dir) == 0 { 178 dir = []rune{'.'} 179 } 180 dir = append(dir, '/') 181 dir = append(dir, path_...) 182 dir = runes.CleanPath(dir) 183 } 184 185 c, err := complete.Complete(string(dir), string(str)) 186 if err != nil { 187 alog.Printf("error attempting completion: %v\n", err) 188 return nil 189 } 190 191 if !c.Progress { 192 sep := "" 193 if len(dir) > 0 && dir[len(dir)-1] != '/' { 194 sep = "/" 195 } 196 more := "" 197 if c.NumMatch == 0 { 198 more = ": no matches in:" 199 } 200 alog.Printf("%s%s%s*%s\n", string(dir), sep, string(str), more) 201 for i := 0; i < len(c.Files); i++ { 202 alog.Printf(" %s\n", c.Files[i]) 203 } 204 } 205 206 var rp []rune 207 if c.Progress { 208 rp = []rune(c.Text) 209 } 210 211 return rp 212 } 213 214 func fileload(f *wind.File, p0 int, fd *os.File, nulls *bool, h io.Writer) int { 215 if f.Seq() > 0 { 216 util.Fatal("undo in file.load unimplemented") 217 } 218 return fileload1(f, p0, fd, nulls, h) 219 }