github.com/grzegorz-zur/bm@v0.0.0-20240312214136-6fc133e3e2c0/file.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  	"unicode/utf8"
     7  )
     8  
     9  // File represents open file.
    10  type File struct {
    11  	Path     string
    12  	content  string
    13  	location int
    14  	mark     int
    15  	area     Area
    16  	time     time.Time
    17  	changed  bool
    18  	history  *History
    19  }
    20  
    21  const (
    22  	// Tab is tabulation rune.
    23  	Tab = '\t'
    24  	// EOL is end of line rune.
    25  	EOL = '\n'
    26  )
    27  
    28  // Archive makes a record in history.
    29  func (file *File) Archive() {
    30  	file.history.Archive(file.content, file.location)
    31  }
    32  
    33  // SwitchVersion switches between versions from history.
    34  func (file *File) SwitchVersion(dir Direction) {
    35  	file.content, file.location = file.history.Switch(dir)
    36  }
    37  
    38  // Select sets selection position.
    39  func (file *File) Select() {
    40  	file.mark = file.location
    41  }
    42  
    43  // Copy returns selected content.
    44  func (file *File) Copy() string {
    45  	content, _, _ := file.copy()
    46  	return content
    47  }
    48  
    49  // Cut cuts selected content.
    50  func (file *File) Cut() string {
    51  	content, from, to := file.copy()
    52  	file.Remove(from, to)
    53  	return content
    54  }
    55  
    56  func (file *File) copy() (content string, from, to int) {
    57  	from, to = file.mark, file.location
    58  	if from > to {
    59  		from, to = to, from
    60  	}
    61  	_, size := file.next(to)
    62  	to += size
    63  	return file.content[from:to], from, to
    64  }
    65  
    66  // Render renders file content.
    67  func (file *File) Render(view *View) error {
    68  	position := file.position()
    69  	file.area = file.area.Resize(view.Size).Shift(position)
    70  	line, column := 0, 0
    71  	for location, rune := range file.content {
    72  		if file.area.Contains(Position{line, column}) {
    73  			rline := line - file.area.Top
    74  			rcolumn := column - file.area.Left
    75  			if view.Visible {
    76  				view.Content[rline][rcolumn] = visible(rune)
    77  			} else {
    78  				view.Content[rline][rcolumn] = rune
    79  			}
    80  			if view.Select {
    81  				view.Selection[rline][rcolumn] = file.selected(location)
    82  			}
    83  		}
    84  		column++
    85  		if rune == EOL {
    86  			line++
    87  			column = 0
    88  		}
    89  		if file.area.Bottom <= line {
    90  			break
    91  		}
    92  	}
    93  	view.Position = Position{
    94  		Line:   position.Line - file.area.Top,
    95  		Column: position.Column - file.area.Left,
    96  	}
    97  	view.Status = fmt.Sprintf("%s %d:%d", file.Path, position.Line+1, position.Column+1)
    98  	view.Cursor = CursorContent
    99  	return nil
   100  }
   101  
   102  func (file *File) position() Position {
   103  	line, column := 0, 0
   104  	for location, rune := range file.content {
   105  		if location == file.location {
   106  			break
   107  		}
   108  		column++
   109  		if rune == EOL {
   110  			line++
   111  			column = 0
   112  		}
   113  	}
   114  	return Position{line, column}
   115  }
   116  
   117  func (file *File) selected(location int) bool {
   118  	return file.mark <= location && location < file.location ||
   119  		file.location < location && location <= file.mark
   120  }
   121  
   122  func (file *File) last() (rune rune, size int) {
   123  	return file.previous(file.location)
   124  }
   125  
   126  func (file *File) current() (rune rune, size int) {
   127  	return file.next(file.location)
   128  }
   129  
   130  func (file *File) previous(location int) (rune rune, size int) {
   131  	return utf8.DecodeLastRuneInString(file.content[:location])
   132  }
   133  
   134  func (file *File) next(location int) (rune rune, size int) {
   135  	return utf8.DecodeRuneInString(file.content[location:])
   136  }