2
0
Fork 0
mirror of https://github.com/vlang/vid synced 2020-06-03 18:34:23 +00:00
vid/view.v
2020-05-23 08:48:02 +02:00

732 lines
13 KiB
V

// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by a GPL license
// that can be found in the LICENSE file.
module main
import os
import strings
struct View {
mut:
padding_left int
from int
x int
y int
prev_x int
path string
short_path string
prev_path string // for tt
lines []string
page_height int
vstart int
vend int // visual borders
changed bool
error_y int
vid &Vid
prev_y int
hash_comment bool
hl_on bool
}
fn (vid &Vid) new_view() View {
res := View {
padding_left: 0
path: ''
from: 0
y: 0
x: 0
prev_x: 0
page_height: vid.page_height
vstart: -1
vend: -1
vid: vid
error_y: -1
prev_y: -1
}
return res
}
// `mut res := word.clone()` ==>
// ['mut' 'res' 'word' 'clone']
fn get_clean_words(line string) []string {
mut res := []string{}
mut i := 0
for i < line.len {
// Skip bad first
for i < line.len && !is_alpha_underscore(int(line[i])) {
i++
}
// Read all good
start2 := i
for i < line.len && is_alpha_underscore(int(line[i])) {
i++
}
// End of word, save it
word := line[start2..i]
res << word
i++
}
return res
}
fn (mut view View) open_file(path string) {
println('open file "$path"')
if path == '' {
return
}
mut vid := view.vid
mut is_new := false
if path != view.path {
is_new = true
// Save cursor pos (Y)
// view.vid.file_y_pos.set(view.path, view.y)
view.prev_path = view.path
}
mut lines := []string{}
if rlines := os.read_lines(path) { lines = rlines }
view.lines = lines
// get words map
if view.lines.len < 1000 {
println('getting words')
// ticks := glfw.get_time()
for line in view.lines {
// words := line.split(' ')
words := get_clean_words(line)
for word in words {
// clean_word := get_clean_word(word)
// if clean_word == '' {
// continue
// }
if !(word in vid.words) {
vid.words << word
}
}
}
// took := glfw.get_time() - ticks
}
// Empty file, handle it
if view.lines.len == 0 {
view.lines << ''
}
view.path = path
view.short_path = path.replace(view.vid.workspace, '')
if view.short_path.starts_with('/') {
view.short_path = view.short_path[1..]
}
// Calc padding_left
nr_lines := view.lines.len
s := '$nr_lines'
view.padding_left = s.len * vid.char_width + 8
view.vid.save_session()
// Go to old y
if is_new {
tmp := view.y
if view.prev_y > -1 {
view.y = view.prev_y
view.zz()
}
view.prev_y = tmp
}
if false {
y := view.vid.file_y_pos[view.path]
if y > 0 {
view.y = y
}
}
view.hash_comment = !view.path.ends_with('.v')
view.hl_on = !view.path.ends_with('.md')
view.changed = false
}
fn (mut view View) reopen() {
view.open_file(view.path)
view.changed = false
}
fn (mut view View) save_file() {
view.x = view.x // TODO remove once the mut bug is fixed
if view.path == '' {
return
}
path := view.path
println('saving file "$path"')
println('lines.len=$view.lines.len')
//line0 := view.lines[0]
//println('line[0].len=$line0.len')
mut file := os.create(path) or {
panic('fail')
}
for line in view.lines {
file.writeln(line.trim_right(' \t'))
}
file.close()
go view.format_file()
}
fn (mut view View) format_file() {
path := view.path
// Run formatters
if path.ends_with('.go') {
println('running goimports')
os.system('goimports -w "$path"')
}
else if path.ends_with('.scss') {
css := path.replace('.scss', '.css')
os.system('sassc "$path" > "$css"')
}
else if path.ends_with('.v') && path.contains('vlib/v/') {
os.system('v fmt -w $path')
}
view.reopen()
// update git diff
view.vid.get_git_diff()
view.changed = false
//println('end of save file()')
//println('_lines.len=$view.lines.len')
//line0_ := view.lines[0]
//println('_line[0].len=$line0_.len')
}
fn (view &View) line() string {
if view.y < 0 || view.y >= view.lines.len {
return ''
}
return view.lines[view.y]
}
fn (view &View) uline() ustring {
return view.line().ustring()
}
fn (view &View) char() int {
line := view.line()
if line.len > 0 {
return int(line[view.x])
}
return 0
}
fn (mut view View) set_line(newline string) {
if view.y + 1 > view.lines.len {
view.lines << newline
}
else {
view.lines[view.y] = newline
}
view.changed = true
}
fn (mut view View) j() {
if view.lines.len == 0 {
return
}
view.y++
// Reached end
if view.y >= view.lines.len {
view.y = view.lines.len - 1
return
}
// Scroll
if view.y >= view.from + view.page_height {
view.from++
}
// Line below is shorter, move to the end of it
line := view.line()
if view.x > line.len - 1 {
view.prev_x = view.x
view.x = line.len - 1
if view.x < 0 {
view.x = 0
}
}
}
fn (mut view View) k() {
if view.y <= 0 {
return
}
view.y--
if view.y < view.from && view.y > 0 {
view.from--
}
// Line above is shorter, move to the end of it
line := view.line()
if view.x > line.len - 1 {
view.prev_x = view.x
view.x = line.len - 1
if view.x < 0 {
view.x = 0
}
}
}
fn (mut view View) shift_h() {
view.y = view.from
}
fn (mut view View) move_to_page_bot() {
view.y = view.from + view.page_height - 1
}
fn (mut view View) l() {
line := view.line()
if view.x < line.len - 1 {
view.x++
}
}
fn (mut view View) shift_g() {
view.y = view.lines.len - 1
view.from = view.y - view.page_height + 1
if view.from < 0 {
view.from = 0
}
}
fn (mut view View) shift_a() {
line := view.line()
view.set_line('$line ')
view.x = view.uline().len - 1
}
fn (mut view View) shift_i() {
view.x = 0
for view.char() == view.vid.cfg.tab {
view.x++
}
}
fn (mut view View) gg() {
view.from = 0
view.y = 0
}
fn (mut view View) shift_f() {
view.from += view.page_height
if view.from >= view.lines.len {
view.from = view.lines.len - 1
}
view.y = view.from
}
fn (mut view View) shift_b() {
view.from -= view.page_height
if view.from < 0 {
view.from = 0
}
view.y = view.from
}
fn (mut view View) dd() {
if view.lines.len != 0 {
mut vid := view.vid
vid.prev_key = -1
vid.prev_cmd = 'dd'
vid.ylines = []
vid.ylines << view.line()
view.lines.delete(view.y)
view.changed = true
}
}
fn (mut view View) shift_right() {
// No selection, shift current line
if view.vstart == -1 {
view.set_line('\t${view.line()}')
return
}
for i := view.vstart; i <= view.vend; i++ {
line := view.lines[i]
view.lines[i] = '\t$line'
}
}
fn (mut view View) shift_left() {
if view.vstart == -1 {
line := view.line()
if !line.starts_with('\t') {
return
}
view.set_line(line[1..])
return
}
for i := view.vstart; i <= view.vend; i++ {
line := view.lines[i]
if !line.starts_with('\t') {
continue
}
view.lines[i] = line[1..]
}
}
fn (mut v View) delete_char() {
u := v.uline()
if v.x >= u.len {
return
}
left := u.left(v.x)
right := u.right(v.x + 1)
new_line := left + right
v.set_line(new_line)
if v.x >= new_line.len {
v.x = new_line.len - 1
}
}
fn (mut view View) shift_c() string {
line := view.line()
s := line[..view.x]
deleted := line[view.x..]
view.set_line('${s} ')
view.x = s.len
return deleted
}
fn (mut view View) insert_text(s string) {
line := view.line()
if line.len == 0 {
view.set_line('$s ')
}
else {
if view.x >= line.len {
view.x = line.len
}
uline := line.ustring()
if view.x >= uline.len {
return
}
left := uline.substr(0,view.x)
right := uline.substr(view.x,uline.len)
// Insert chat in the middle
res := '${left}${s}${right}'
view.set_line(res)
}
view.x += s.ustring().len
view.changed = true
}
fn (mut view View) backspace(go_up bool) {
if view.x == 0 {
if go_up && view.y > 0 {
view.x = 0
view.y--
view.x = view.lines[view.y].len
view.lines.delete(view.y+1)
view.changed = true
}
return
}
uline := view.uline()
left := uline.left(view.x - 1)
mut right := ''
if view.x < uline.len {
right = uline.right(view.x)
}
view.set_line('${left}${right}')
view.x--
}
fn (mut view View) yy() {
mut ylines := []string{}
ylines << (view.line())
view.vid.ylines = ylines
}
fn (mut view View) p() {
for line in view.vid.ylines {
view.o()
view.set_line(line)
}
}
fn (mut view View) shift_o() {
view.o_generic(0)
}
fn (mut view View) o() {
view.o_generic(1)
}
fn (mut view View) o_generic(delta int) {
view.y += delta
// Insert the same amount of spaces/tabs as in prev line
prev_line := if view.lines.len == 0 || view.y == 0 {
''
} else {
view.lines[view.y - 1]
}
mut nr_spaces := 0
mut nr_tabs := 0
mut i := 0
for i < prev_line.len && (prev_line[i] == ` ` || prev_line[i] == `\t`) {
if prev_line[i] == ` ` {
nr_spaces++
}
if prev_line[i] == `\t` {
nr_tabs++
}
i++
}
mut new_line := strings.repeat(`\t`, nr_tabs) + strings.repeat(` `, nr_spaces)
if prev_line.ends_with('{') || prev_line.ends_with('{ ') {
new_line += '\t '
} else if !new_line.ends_with(' ') {
new_line += ' '
}
view.x = new_line.len-1
if view.y >= view.lines.len {
view.lines << new_line
}
else {
view.lines.insert(view.y, new_line)
}
view.changed = true
}
fn (mut view View) enter() {
// Create new line
// And move everything to the right of the cursor to it
pos := view.x
line := view.line()
if pos >= line.len-1 && line != '' && line != ' ' {
// {} insertion
if line.ends_with('{ ') {
view.o()
view.x--
view.insert_text('}')
view.y--
view.o()
//view.insert_text('\t')
//view.x = 0
} else {
view.o()
}
return
}
//if line == '' {
//view.o()
//return
//}
uline := line.ustring()
mut right := ''
if pos < uline.len{
right = uline.right(pos)
}
left := uline.left(pos)
view.set_line(left)
view.o()
view.set_line(right)
view.x = 0
}
fn (mut view View) join() {
if view.y == view.lines.len - 1 {
return
}
// Add next line to current line
line := view.line()
second_line := view.lines[view.y + 1]
joined := line + second_line
view.set_line(joined)
view.y++
view.dd()
view.y--
// view.prev_cmd = "J"
}
fn (mut v View) y_visual() {
mut ylines := []string{}
for i := v.vstart; i <= v.vend; i++ {
ylines << v.lines[i]
}
mut vid := v.vid
vid.ylines = ylines
// Copy YY to clipboard TODO
// mainWindow.SetClipboardString(strings.Join(ylines, "\n"))
v.vstart = -1
v.vend = -1
}
fn (mut view View) d_visual() {
view.y_visual()
for i := 0; i < view.vid.ylines.len; i++ {
view.lines.delete(view.y)
}
}
fn (mut view View) cw() {
mut vid := view.vid
view.dw()
vid.prev_cmd = 'cw'
view.vid.set_insert()
}
fn (mut view View) dw() {
mut vid := view.vid
typ := is_alpha(view.char())
// While cur char has the same type - delete it
for {
line := view.line()
if view.x <= 0 || view.x >= line.len - 1 {
break
}
if typ == is_alpha(view.char()) {
println('del x=$view.x len=$line.len')
view.delete_char()
}
else {
break
}
}
// Delete whitespace after the deleted word
for is_whitespace(view.char()) {
line := view.line()
if view.x <= 0 || view.x >= line.len {
break
}
view.delete_char()
}
vid.prev_cmd = 'dw'
}
// TODO COPY PASTA
// same as cw but deletes underscores
fn (mut view View) ce() {
mut vid := view.vid
view.de()
vid.prev_cmd = 'ce'
view.vid.set_insert()
}
fn (mut view View) w() {
line := view.line()
typ := is_alpha_underscore(view.char())
// Go to end of current word
for view.x < line.len - 1 && typ == is_alpha_underscore(view.char()) {
view.x++
}
// Go to start of next word
for view.x < line.len - 1 && view.char() == 32 {
view.x++
}
}
fn (mut view View) b() {
// line := view.line()
// Go to start of prev word
for view.x > 0 && view.char() == 32 {
view.x--
}
typ := is_alpha_underscore(view.char())
// Go to start of current word
for view.x > 0 && typ == is_alpha_underscore(view.char()) {
view.x--
}
}
fn (mut view View) de() {
mut vid := view.vid
typ := is_alpha_underscore(view.char())
// While cur char has the same type - delete it
for {
line := view.line()
if view.x >= 0 && view.x < line.len && typ == is_alpha_underscore(view.char()) {
view.delete_char()
}
else {
break
}
}
vid.prev_cmd = 'de'
}
fn (mut view View) zz() {
view.from = view.y - view.vid.page_height / 2
if view.from < 0 {
view.from = 0
}
}
fn (mut view View) r(s string) {
view.delete_char()
view.insert_text(s)
view.x--
}
fn (mut view View) tt() {
if view.prev_path == '' {
return
}
mut vid := view.vid
vid.prev_key = -1
view.open_file(view.prev_path)
}
fn (mut view View) move_to_line(line int) {
view.from = line
view.y = line
view.zz()
}
// Fit lines into 80 chars
fn (mut view View) gq() {
mut vid := view.vid
if vid.mode != .visual {
return
}
view.y_visual()
max := vid.max_chars(0)
// Join all selected lines into a single string
joined := vid.ylines.join('\n')
// Delete what we selected
for yline in vid.ylines {
if yline == '' {
continue
}
view.lines.delete(view.y)
}
new_lines := break_text(joined, max - 1)
for line in new_lines {
view.insert_text(line)
view.o()
}
vid.mode = .normal
}
fn is_alpha(r byte) bool {
return ((r >= `a` && r <= `z`) || (r >= `A` && r <= `Z`) ||
(r >= `0` && r <= `9`))
}
fn is_whitespace(r byte) bool {
return r == ` ` || r == `\t`
}
fn is_alpha_underscore(r int) bool {
return (is_alpha(r) || byte(r) == `_` || byte(r) == `#` || byte(r) == `$`)
}
fn break_text(s string, max int) []string {
mut lines := []string{}
mut start := 0
for i := 0; i < s.len; i++ {
if i == s.len - 1 {
// Include the very last char
lines << s[start..i + 1]
break
}
if i - start >= max {
lines << s[start..i]
start = i
}
}
return lines
}