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  }