Add command templates

This commit is contained in:
2026-03-28 21:56:49 +09:00
parent 83951e4138
commit 325c3270be
16 changed files with 1593 additions and 222 deletions

View File

@@ -59,6 +59,7 @@ Command categories:
* ) : Move cursor forward by sentence. (MoveBySentence)
* ( : Move cursor backward by sentence. (MoveBackwardBySentence)
* } : Move cursor forward by paragraph. (MoveByParagraph)
(Note: Proper vi respects nroff/troff directives, but levi doesn't.)
* { : Move cursor backward by paragraph. (MoveBackwardByParagraph)
(Note: Proper vi respects nroff/troff directives, but levi doesn't.)
* ]] : Move cursor forward by section. (MoveBySection)

View File

@@ -2,6 +2,23 @@ package editor
type CmdKind int
type Loc struct {
Row int
Col int
}
type Cmd struct {
Kind CmdKind
Num int
Letter rune
Pat string
Reg rune
Start Loc
End Loc
StartRow int
EndRow int
}
const (
CmdInvalid CmdKind = iota
@@ -97,10 +114,10 @@ const (
CmdOpDelete
CmdOpDeleteBefore
CmdOpDeleteLine
CmdOpDelteRegion
CmdOpDeleteRegion
CmdOpDeleteLineRegion
CmdOpDeleteWord
CmdOpDelteToEnd
CmdOpDeleteToEnd
CmdOpChangeLine
CmdOpChangeRegion
@@ -142,8 +159,3 @@ const (
CmdPromptQuitAll
CmdPromptForceQuitAll
)
type Cmd struct {
Kind CmdKind
Num int
}

110
internal/editor/draw.go Normal file
View File

@@ -0,0 +1,110 @@
package editor
import (
"unicode/utf8"
"tea.kareha.org/lab/termi"
)
func (ed *Editor) LineHeight(line string) int {
rc := utf8.RuneCountInString(line)
width := termi.StringWidth(line, rc)
return 1 + max(width-1, 0)/ed.w
}
func (ed *Editor) DrawBuffer() {
y := 0
for i := ed.vrow; i < len(ed.lines); i++ {
line := ed.Line(i)
termi.MoveCursor(0, y)
termi.Draw(line)
y += ed.LineHeight(line)
if y >= ed.h-1 {
break
}
}
for ; y < ed.h-1; y++ {
termi.MoveCursor(0, y)
termi.Draw("~")
}
}
func (ed *Editor) DrawStatus() {
var m string
switch ed.mode {
case ModeCommand:
m = "vi command"
case ModeInsert:
m = "vi insert"
case ModeSearch:
m = "vi search"
case ModePrompt:
m = "vi prompt"
default:
panic("invalid mode")
}
termi.MoveCursor(0, ed.h-1)
if ed.message != "" {
termi.EnableInvert()
termi.Print(ed.message)
termi.DisableInvert()
ed.message = ""
} else {
termi.Printf("[%s] %s %d,%d %s", ed.parser.Cache(), m, ed.row, ed.col, ed.path)
}
}
func (ed *Editor) UpdateCursor() {
// XXX approximation
width := termi.StringWidth(ed.CurrentLine(), ed.col)
ed.x = width % ed.w
dy := width / ed.w
if ed.row < ed.vrow {
ed.vrow = ed.row
}
y := 0
for i := ed.vrow; i < ed.row; i++ {
y += ed.LineHeight(ed.lines[i])
}
ed.y = y + dy
for ed.y >= ed.h-1 {
ed.vrow++
y := 0
for i := ed.vrow; i < ed.row; i++ {
y += ed.LineHeight(ed.lines[i])
}
ed.y = y + dy
}
}
func (ed *Editor) Repaint() {
w, h := termi.Size()
ed.w = w
ed.h = h
termi.HideCursor()
termi.Clear()
termi.HomeCursor()
ed.UpdateCursor()
ed.DrawBuffer()
ed.DrawStatus()
termi.MoveCursor(ed.x, ed.y)
termi.ShowCursor()
}
func (ed *Editor) Draw() {
ed.Repaint()
}

41
internal/editor/edit.go Normal file
View File

@@ -0,0 +1,41 @@
package editor
//////////////////////
// Editing Commands //
//////////////////////
// r : Replace single character under cursor.
func (ed *Editor) EditReplace(letter rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("EditReplace")
}
// J : Join current line with next line.
func (ed *Editor) EditJoin(n int) {
ed.EnsureCommand()
ed.Unimplemented("EditJoin")
}
// >> : Indent current line.
func (ed *Editor) EditIndent(n int) {
ed.EnsureCommand()
ed.Unimplemented("EditIndent")
}
// << : Outdent current line.
func (ed *Editor) EditOutdent(n int) {
ed.EnsureCommand()
ed.Unimplemented("EditOutdent")
}
// > <mv> : Indent region from current cursor to destination of motion <mv>.
func (ed *Editor) EditIndentRegion(start Loc, end Loc) {
ed.EnsureCommand()
ed.Unimplemented("EditIndentRegion")
}
// < <mv> : Outdent region from current cursor to destination of motion <mv>.
func (ed *Editor) EditOutdentRegion(start Loc, end Loc) {
ed.EnsureCommand()
ed.Unimplemented("EditOutdentRegion")
}

View File

@@ -146,6 +146,24 @@ func (ed *Editor) InsertRune(r rune) {
ed.col = ed.inp.Column()
}
func (ed *Editor) EnsureCommand() {
if ed.mode == ModeCommand {
return
}
if ed.mode == ModeInsert {
ed.lines[ed.row] = ed.inp.Line()
ed.inp.Reset()
ed.mode = ModeCommand
ed.MoveLeft(1)
return
}
}
func (ed *Editor) Ring(message string) {
ed.message = message
}
func (ed *Editor) Unimplemented(name string) {
ed.Ring("not implemented (" + name + ")")
}

41
internal/editor/find.go Normal file
View File

@@ -0,0 +1,41 @@
package editor
////////////////////////////////
// Character Finding Commands //
////////////////////////////////
// f<letter> : Find character <letter> forward in current line.
func (ed *Editor) FindForward(letter rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("FindForward")
}
// F<letter> : Find character <letter> backward in current line.
func (ed *Editor) FindBackward(letter rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("FindBackward")
}
// t<letter> : Find before character <letter> forward in current line.
func (ed *Editor) FindBeforeForward(letter rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("FindBeforeForward")
}
// T<letter> : Find before character <letter> backward in current line.
func (ed *Editor) FindBeforeBackward(letter rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("FindBeforeBackward")
}
// ; : Find next match.
func (ed *Editor) FindNextMatch(n int) {
ed.EnsureCommand()
ed.Unimplemented("FindNextMatch")
}
// , : Find previous match.
func (ed *Editor) FindPrevMatch(n int) {
ed.EnsureCommand()
ed.Unimplemented("FindPrevMatch")
}

View File

@@ -1,19 +1,23 @@
package editor
////////////////////////
// Insertion Commands //
////////////////////////
//
// Enter Insert Mode
//
// i : Switch to insert mode before cursor.
func (ed *Editor) InsertBefore(n int) {
if ed.mode == ModeInsert {
panic("invalid state")
}
ed.EnsureCommand()
ed.inp.Init(ed.CurrentLine(), ed.col)
ed.mode = ModeInsert
}
// a : Switch to insert mode after cursor.
func (ed *Editor) InsertAfter(n int) {
if ed.mode == ModeInsert {
panic("invalid state")
}
ed.EnsureCommand()
rc := ed.RuneCount()
if ed.col >= rc-1 {
ed.col = rc
@@ -22,3 +26,37 @@ func (ed *Editor) InsertAfter(n int) {
}
ed.InsertBefore(n)
}
// I : Switch to insert mode before first non-blank character of current line.
func (ed *Editor) InsertBeforeNonBlank(n int) {
ed.EnsureCommand()
ed.Unimplemented("InsertBeforeNonBlank")
}
// A : Switch to insert mode after end of current line.
func (ed *Editor) InsertAfterEnd(n int) {
ed.EnsureCommand()
ed.Unimplemented("InsertAfterEnd")
}
// R : Switch to replace (overwrite) mode.
func (ed *Editor) InsertOverwrite(n int) {
ed.EnsureCommand()
ed.Unimplemented("InsertOverwrite")
}
//
// Open Line
//
// o : Open a new line below and switch to insert mode.
func (ed *Editor) InsertOpenBelow(n int) {
ed.EnsureCommand()
ed.Unimplemented("InsertOpenBelow")
}
// O : Open a new line above and switch to insert mode.
func (ed *Editor) InsertOpenAbove(n int) {
ed.EnsureCommand()
ed.Unimplemented("InsertOpenAbove")
}

View File

@@ -4,16 +4,6 @@ import (
"tea.kareha.org/lab/termi"
)
func (ed *Editor) ExitInsert() {
if ed.mode != ModeInsert {
panic("invalid state")
}
ed.lines[ed.row] = ed.inp.Line()
ed.inp.Reset()
ed.mode = ModeCommand
ed.MoveLeft(1)
}
func (ed *Editor) InsertNewline() {
if ed.mode != ModeInsert {
panic("invalid state")
@@ -85,7 +75,7 @@ func (ed *Editor) Main() {
case termi.KeyRune:
switch key.Rune {
case termi.RuneEscape:
ed.ExitInsert()
ed.EnsureCommand()
case termi.RuneEnter:
ed.InsertNewline()
case termi.RuneBackspace:
@@ -96,16 +86,12 @@ func (ed *Editor) Main() {
ed.InsertRune(key.Rune)
}
case termi.KeyUp:
ed.ExitInsert()
ed.MoveUp(1)
case termi.KeyDown:
ed.ExitInsert()
ed.MoveDown(1)
case termi.KeyRight:
ed.ExitInsert()
ed.MoveRight(1)
case termi.KeyLeft:
ed.ExitInsert()
ed.MoveLeft(1)
default:
ed.Ring("unknown key")

43
internal/editor/mark.go Normal file
View File

@@ -0,0 +1,43 @@
package editor
//////////////////////
// Marking Commands //
//////////////////////
//
// Set Mark / Move to Mark
//
// m<letter> : Mark current cursor position labelled by <letter>.
func (ed *Editor) MarkSet(letter rune) {
ed.EnsureCommand()
ed.Unimplemented("MarkSet")
}
// `<letter> : Move cursor to marked position labelled by <letter>.
func (ed *Editor) MarkMoveTo(letter rune) {
ed.EnsureCommand()
ed.Unimplemented("MarkMoveTo")
}
// '<letter> : Move cursor to marked line labelled by <letter>.
func (ed *Editor) MarkMoveToLine(letter rune) {
ed.EnsureCommand()
ed.Unimplemented("MarkMoveToLine")
}
//
// Move by Context
//
// “ : Move cursor to previous position in context.
func (ed *Editor) MarkBack() {
ed.EnsureCommand()
ed.Unimplemented("MarkBack")
}
// ” : Move cursor to previous line in context.
func (ed *Editor) MarkBackToLine() {
ed.EnsureCommand()
ed.Unimplemented("MarkBackToLine")
}

View File

@@ -1,6 +1,35 @@
package editor
////////////////////////////
// Miscellaneous Commands //
////////////////////////////
// Ctrl-g : Show info such as current cursor position.
func (ed *Editor) MiscShowInfo() {
ed.EnsureCommand()
ed.Unimplemented("MiscShowInfo")
}
// . : Repeat last edit.
func (ed *Editor) MiscRepeat(n int) {
ed.EnsureCommand()
ed.Unimplemented("MiscRepeat")
}
// u : Undo.
func (ed *Editor) MiscUndo(n int) {
ed.EnsureCommand()
ed.Unimplemented("MiscUndo")
}
// U : Restore current line to previous state.
func (ed *Editor) MiscRestore() {
ed.EnsureCommand()
ed.Unimplemented("MiscRestore")
}
// ZZ : Save and quit.
func (ed *Editor) MiscSaveAndQuit() {
ed.EnsureCommand()
ed.quit = true
}

View File

@@ -1,64 +1,62 @@
package editor
/////////////////////
// Motion Commands //
/////////////////////
//
// Move by Character / Move by Line
//
// h : Move cursor left by character.
func (ed *Editor) MoveLeft(n int) {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.col -= n
ed.Confine()
}
// j : Move cursor down by line.
func (ed *Editor) MoveDown(n int) {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.row += n
ed.Confine()
}
// k : Move cursor up by line.
func (ed *Editor) MoveUp(n int) {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.row -= n
ed.Confine()
}
// l : Move cursor right by character.
func (ed *Editor) MoveRight(n int) {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.col += n
ed.Confine()
}
//
// Move in Line
//
// 0 : Move cursor to start of current line.
func (ed *Editor) MoveToStart() {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.col = 0
// col is already confined
ed.Confine() // redundant
}
// $ : Move cursor to end of current line.
func (ed *Editor) MoveToEnd() {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.col = ed.RuneCount() - 1
ed.Confine()
}
// ^ : Move cursor to first non-blank character of current line.
func (ed *Editor) MoveToNonBlank() {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
line := ed.CurrentLine()
i := 0
for _, r := range line {
@@ -74,9 +72,149 @@ func (ed *Editor) MoveToNonBlank() {
// <num>| : Move cursor to column <num> of current line.
// (Note: Proper vi's column number is visual-based, but levi' is rune-based.)
func (ed *Editor) MoveToColumn(n int) {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
ed.col = n - 1
ed.Confine()
}
//
// Move by Word / Move by Loose Word
//
// w : Move cursor forward by word.
func (ed *Editor) MoveByWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveByWord")
}
// b : Move cursor backward by word.
func (ed *Editor) MoveBackwardByWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBackwardByWord")
}
// e : Move cursor to end of word.
func (ed *Editor) MoveToEndOfWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToEndOfWord")
}
// W : Move cursor forward by loose word.
func (ed *Editor) MoveByLooseWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveByLooseWord")
}
// B : Move cursor backward by loose word.
func (ed *Editor) MoveBackwardByLooseWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBackwardByLooseWord")
}
// E : Move cursor to end of loose word.
func (ed *Editor) MoveToEndOfLooseWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToEndOfLooseWord")
}
//
// Move by Line
//
// Enter, + : Move cursor to first non-blank character of next line.
func (ed *Editor) MoveToNonBlankOfNextLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToNonBlankOfNextLine")
}
// - : Move cursor to first non-blank character of previous line.
func (ed *Editor) MoveToNonBlankOfPrevLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToNonBlankOfPrevLine")
}
// G : Move cursor to last line.
func (ed *Editor) MoveToLastLine() {
ed.EnsureCommand()
ed.Unimplemented("MoveToLastLine")
}
// <num>G : Move cursor to line <num>.
func (ed *Editor) MoveToLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToLine")
}
//
// Move by Block
//
// ) : Move cursor forward by sentence.
func (ed *Editor) MoveBySentence(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBySentence")
}
// ( : Move cursor backward by sentence.
func (ed *Editor) MoveBackwardBySentence(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBackwardBySentence")
}
// } : Move cursor forward by paragraph.
func (ed *Editor) MoveByParagraph(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveByParagraph")
}
// { : Move cursor backward by paragraph.
func (ed *Editor) MoveBackwardByParagraph(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBackwardByParagraph")
}
// ]] : Move cursor forward by section.
func (ed *Editor) MoveBySection(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBySection")
}
// [[ : Move cursor backward by section.
func (ed *Editor) MoveBackwardBySection(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveBackwardBySection")
}
//
// Move in View
//
// H : Move cursor to top of view.
func (ed *Editor) MoveToTopOfView() {
ed.EnsureCommand()
ed.Unimplemented("MoveToTopOfView")
}
// M : Move cursor to middle of view.
func (ed *Editor) MoveToMiddleOfView() {
ed.EnsureCommand()
ed.Unimplemented("MoveToMiddleOfView")
}
// L : Move cursor to bottom of view.
func (ed *Editor) MoveToBottomOfView() {
ed.EnsureCommand()
ed.Unimplemented("MoveToBottomOfView")
}
// <num>H : Move cursor below <num> lines from top of view.
func (ed *Editor) MoveToBelowTopOfView(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToBelowTopOfView")
}
// <num>L : Move cursor above <num> lines from bottom of view.
func (ed *Editor) MoveToAboveBottomOfView(n int) {
ed.EnsureCommand()
ed.Unimplemented("MoveToAboveBottomOfView")
}

View File

@@ -1,10 +1,78 @@
package editor
///////////////////////////////////////////////
// Operator Commands (Copy / Delte / Change) //
///////////////////////////////////////////////
//
// Copy (Yank)
//
// yy, Y : Copy current line.
func (ed *Editor) OpCopyLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpCopyLine")
}
// y<mv> : Copy region from current cursor to destination of motion <mv>.
func (ed *Editor) OpCopyRegion(start Loc, end Loc) {
ed.EnsureCommand()
ed.Unimplemented("OpCopyRegion")
}
// y<mv> : Copy region from current cursor to destination of motion <mv>.
func (ed *Editor) OpCopyLineRegion(start int, end int) {
ed.EnsureCommand()
ed.Unimplemented("OpCopyLineRegion")
}
// yw : Copy word.
func (ed *Editor) OpCopyWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpCopyWord")
}
// y$ : Copy to end of current line.
func (ed *Editor) OpCopyToEnd(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpCopyToEnd")
}
// "<reg>yy : Copy current line into register <reg>.
func (ed *Editor) OpCopyLineIntoReg(reg rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("OpCopyLineIntoReg")
}
//
// Paste (Put)
//
// p : Paste after cursor.
func (ed *Editor) OpPaste(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpPaste")
}
// P : Paste before cursor.
func (ed *Editor) OpPasteBefore(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpPasteBefore")
}
// "<reg>p : Paste from register <reg>.
func (ed *Editor) OpPasteFromReg(reg rune, n int) {
ed.EnsureCommand()
ed.Unimplemented("OpPasteFromReg")
}
//
// Delete
//
// x : Delete character under cursor.
func (ed *Editor) OpDelete(n int) {
if ed.mode != ModeCommand {
panic("invalid state")
}
ed.EnsureCommand()
if len(ed.CurrentLine()) < 1 {
ed.Ring("nothing to delete, line is empty")
return
@@ -19,3 +87,84 @@ func (ed *Editor) OpDelete(n int) {
}
ed.Confine()
}
// X : Delete character before cursor.
func (ed *Editor) OpDeleteBefore(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpDeleteBefore")
}
// dd : Delete current line.
func (ed *Editor) OpDeleteLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpDeleteLine")
}
// d<mv> : Delete region from current cursor to destination of motion <mv>.
func (ed *Editor) OpDeleteRegion(start Loc, end Loc) {
ed.EnsureCommand()
ed.Unimplemented("OpDeleteRegion")
}
// d<mv> : Delete region from current cursor to destination of motion <mv>.
func (ed *Editor) OpDeleteLineRegion(start int, end int) {
ed.EnsureCommand()
ed.Unimplemented("OpDeleteLineRegion")
}
// dw : Delete word.
func (ed *Editor) OpDeleteWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpDeleteWord")
}
// d$, D : Delete to end of current line.
func (ed *Editor) OpDeleteToEnd(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpDeleteToEnd")
}
//
// Change / Substitute
//
// cc : Change current line.
func (ed *Editor) OpChangeLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpChangeLine")
}
// c<mv> : Change region from current cursor to destination of motion <mv>.
func (ed *Editor) OpChangeRegion(start Loc, end Loc) {
ed.EnsureCommand()
ed.Unimplemented("OpChangeRegion")
}
// c<mv> : Change region from current cursor to destination of motion <mv>.
func (ed *Editor) OpChangeLineRegion(start int, end int) {
ed.EnsureCommand()
ed.Unimplemented("OpChangeLineRegion")
}
// cw : Change word.
func (ed *Editor) OpChangeWord(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpChangeWord")
}
// C : Change to end of current line.
func (ed *Editor) OpChangeToEnd(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpChangeToEnd")
}
// s : Substitute one character under cursor.
func (ed *Editor) OpSubst(n int) {
ed.EnsureCommand()
ed.Unimplemented("OpSubst")
}
// S : Substtute current line (equals cc).
func (ed *Editor) OpSubstLine(n int) {
ed.OpChangeLine(n)
}

View File

@@ -56,12 +56,10 @@ var letterMoveSet = map[rune]struct{}{
'F': {},
't': {},
'T': {},
';': {},
',': {},
}
func (p *Parser) ParseMove(noNum bool, num int, op string, letter rune) (Cmd, bool) {
switch op {
func (p *Parser) ParseMove(noNum bool, num int, mv string, letter rune) (Cmd, bool) {
switch mv {
case "h":
return Cmd{
Kind: CmdMoveLeft,
@@ -95,22 +93,280 @@ func (p *Parser) ParseMove(noNum bool, num int, op string, letter rune) (Cmd, bo
Num: num,
}, true
// TODO
case "w":
return Cmd{
Kind: CmdMoveByWord,
Num: num,
}, true
case "b":
return Cmd{
Kind: CmdMoveBackwardByWord,
Num: num,
}, true
case "e":
return Cmd{
Kind: CmdMoveToEndOfWord,
Num: num,
}, true
case "W":
return Cmd{
Kind: CmdMoveByLooseWord,
Num: num,
}, true
case "B":
return Cmd{
Kind: CmdMoveBackwardByLooseWord,
Num: num,
}, true
case "E":
return Cmd{
Kind: CmdMoveToEndOfLooseWord,
Num: num,
}, true
case "\r", "+":
return Cmd{
Kind: CmdMoveToNonBlankOfNextLine,
Num: num,
}, true
case "-":
return Cmd{
Kind: CmdMoveToNonBlankOfPrevLine,
Num: num,
}, true
case "G":
if noNum {
return Cmd{Kind: CmdMoveToLastLine}, true
} else {
return Cmd{
Kind: CmdMoveToLine,
Num: num,
}, true
}
case ")":
return Cmd{
Kind: CmdMoveBySentence,
Num: num,
}, true
case "(":
return Cmd{
Kind: CmdMoveBackwardBySentence,
Num: num,
}, true
case "}":
return Cmd{
Kind: CmdMoveByParagraph,
Num: num,
}, true
case "{":
return Cmd{
Kind: CmdMoveBackwardByParagraph,
Num: num,
}, true
case "]]":
return Cmd{
Kind: CmdMoveBySection,
Num: num,
}, true
case "[[":
return Cmd{
Kind: CmdMoveBackwardBySection,
Num: num,
}, true
case "H":
if noNum {
return Cmd{Kind: CmdMoveToTopOfView}, true
} else {
return Cmd{
Kind: CmdMoveToBelowTopOfView,
Num: num,
}, true
}
case "M":
return Cmd{Kind: CmdMoveToMiddleOfView}, true
case "L":
if noNum {
return Cmd{Kind: CmdMoveToBottomOfView}, true
} else {
return Cmd{
Kind: CmdMoveToAboveBottomOfView,
Num: num,
}, true
}
}
return p.ParseFind(num, mv, letter)
}
func (p *Parser) ParseLetter(num int, op string, letter rune) (Cmd, bool) {
if letter == 0 {
return Cmd{}, false
}
switch op {
case "m":
return Cmd{
Kind: CmdMarkSet,
Letter: letter,
}, true
case "`":
if letter == '`' {
return Cmd{Kind: CmdMarkBack}, true
} else {
return Cmd{
Kind: CmdMarkMoveTo,
Letter: letter,
}, true
}
case "'":
if letter == '\'' {
return Cmd{Kind: CmdMarkBackToLine}, true
} else {
return Cmd{
Kind: CmdMarkMoveToLine,
Letter: letter,
}, true
}
}
switch op {
case "r":
return Cmd{
Kind: CmdEditReplace,
Num: num,
Letter: letter,
}, true
}
return Cmd{}, false
}
func (p *Parser) ParseLetter(num int, op string, letter rune) (Cmd, bool) {
func (p *Parser) ParseView(num int, op string) (Cmd, bool) {
switch op {
case "m":
return Cmd{}, false // TODO
case "'":
return Cmd{}, false // TODO
case "`":
return Cmd{}, false // TODO
case "r":
return Cmd{}, false // TODO
case "\x06": // Ctrl-f
return Cmd{
Kind: CmdViewDown,
Num: num,
}, true
case "\x02": // Ctrl-b
return Cmd{
Kind: CmdViewUp,
Num: num,
}, true
case "\x04": // Ctrl-d
return Cmd{
Kind: CmdViewDownHalf,
Num: num,
}, true
case "\x15": // Ctrl-u
return Cmd{
Kind: CmdViewUpHalf,
Num: num,
}, true
case "\x19": // Ctrl-y
return Cmd{
Kind: CmdViewDownLine,
Num: num,
}, true
case "\x05": // Ctrl-e
return Cmd{
Kind: CmdViewUpLine,
Num: num,
}, true
case "z\r":
return Cmd{Kind: CmdViewToTop}, true
case "z.":
return Cmd{Kind: CmdViewToMiddle}, true
case "z-":
return Cmd{Kind: CmdViewToBottom}, true
case "\x0c": // Ctrl-l
return Cmd{Kind: CmdViewRedraw}, true
}
return Cmd{}, false
}
func (p *Parser) ParseSearch(op string, pat string) (Cmd, bool) {
switch op {
case "/":
if pat == "" {
return Cmd{Kind: CmdSearchRepeatForward}, true
} else {
return Cmd{
Kind: CmdSearchForward,
Pat: pat,
}, true
}
case "?":
if pat == "" {
return Cmd{Kind: CmdSearchRepeatBackward}, true
} else {
return Cmd{
Kind: CmdSearchBackward,
Pat: pat,
}, true
}
case "n":
return Cmd{Kind: CmdSearchNextMatch}, true
case "N":
return Cmd{Kind: CmdSearchPrevMatch}, true
}
return Cmd{}, false
}
func (p *Parser) ParseFind(num int, op string, letter rune) (Cmd, bool) {
switch op {
case "f":
if letter == 0 {
return Cmd{}, false
}
return Cmd{
Kind: CmdFindForward,
Num: num,
Letter: letter,
}, true
case "F":
if letter == 0 {
return Cmd{}, false
}
return Cmd{
Kind: CmdFindBackward,
Num: num,
Letter: letter,
}, true
case "t":
if letter == 0 {
return Cmd{}, false
}
return Cmd{
Kind: CmdFindBeforeForward,
Num: num,
Letter: letter,
}, true
case "T":
if letter == 0 {
return Cmd{}, false
}
return Cmd{
Kind: CmdFindBeforeBackward,
Num: num,
Letter: letter,
}, true
case ";":
return Cmd{
Kind: CmdFindNextMatch,
Num: num,
}, true
case ",":
return Cmd{
Kind: CmdFindPrevMatch,
Num: num,
}, true
}
return Cmd{}, false
@@ -128,19 +384,55 @@ func (p *Parser) ParseInsert(num int, op string) (Cmd, bool) {
Kind: CmdInsertAfter,
Num: num,
}, true
case "I":
return Cmd{
Kind: CmdInsertBeforeNonBlank,
Num: num,
}, true
case "A":
return Cmd{
Kind: CmdInsertAfterEnd,
Num: num,
}, true
case "R":
return Cmd{
Kind: CmdInsertOverwrite,
Num: num,
}, true
// TODO
case "o":
return Cmd{
Kind: CmdInsertOpenBelow,
Num: num,
}, true
case "O":
return Cmd{
Kind: CmdInsertOpenAbove,
Num: num,
}, true
}
return Cmd{}, false
}
func (p *Parser) ParseMisc(op string) (Cmd, bool) {
func (p *Parser) ParseMisc(num int, op string) (Cmd, bool) {
switch op {
case "\x07": // Ctrl-g
return Cmd{Kind: CmdMiscShowInfo}, true
case ".":
return Cmd{
Kind: CmdMiscRepeat,
Num: num,
}, true
case "u":
return Cmd{
Kind: CmdMiscUndo,
Num: num,
}, true
case "U":
return Cmd{Kind: CmdMiscRestore}, true
case "ZZ":
return Cmd{Kind: CmdMiscSaveAndQuit}, true
// TODO
}
return Cmd{}, false
@@ -148,19 +440,191 @@ func (p *Parser) ParseMisc(op string) (Cmd, bool) {
func (p *Parser) ParseOp(reg rune, num int, op string, noSubnum bool, subnum int, mv string) (Cmd, bool) {
switch op {
case "yy", "Y":
if reg == 0 {
return Cmd{
Kind: CmdOpCopyLine,
Num: num,
}, true
} else {
return Cmd{
Kind: CmdOpCopyLineIntoReg,
Num: num,
Reg: reg,
}, true
}
case "y":
/*
return Cmd{
Kind: CmdOpCopyRegion,
Start: Loc{}, // TODO
End: Loc{}, // TODO
}, true
return Cmd{
Kind: CmdOpCopyLineRegion,
StartRow: 0, // TODO
EndRow: 0, // TODO
}, true
*/
case "yw":
return Cmd{
Kind: CmdOpCopyWord,
Num: num,
}, true
case "y$":
return Cmd{
Kind: CmdOpCopyToEnd,
Num: num,
}, true
case "p":
if reg == 0 {
return Cmd{
Kind: CmdOpPaste,
Num: num,
}, true
} else {
return Cmd{
Kind: CmdOpPasteFromReg,
Num: num,
Reg: reg,
}, true
}
case "P":
return Cmd{
Kind: CmdOpPasteBefore,
Num: num,
}, true
case "x":
return Cmd{
Kind: CmdOpDelete,
Num: num,
}, true
case "X":
return Cmd{
Kind: CmdOpDeleteBefore,
Num: num,
}, true
case "dd":
return Cmd{
Kind: CmdOpDeleteLine,
Num: num,
}, true
case "d":
/*
return Cmd{
Kind: CmdOpDeleteRegion,
Start: Loc{}, // TODO
End: Loc{}, // TODO
}, true
return Cmd{
Kind: CmdOpDeleteLineRegion,
StartRow: 0, // TODO
EndRow: 0, // TODO
}, true
*/
case "dw":
return Cmd{
Kind: CmdOpDeleteWord,
Num: num,
}, true
case "d$", "D":
return Cmd{
Kind: CmdOpDeleteToEnd,
Num: num,
}, true
// TODO
case "cc":
return Cmd{
Kind: CmdOpChangeLine,
Num: num,
}, true
case "c":
/*
return Cmd{
Kind: CmdOpChangeRegion,
Start: Loc{}, // TODO
End: Loc{}, // TODO
}, true
return Cmd{
Kind: CmdOpChangeLineRegion,
StartRow: 0, // TODO
EndRow: 0, // TODO
}, true
*/
case "cw":
return Cmd{
Kind: CmdOpChangeWord,
Num: num,
}, true
case "C":
return Cmd{
Kind: CmdOpChangeToEnd,
Num: num,
}, true
case "s":
return Cmd{
Kind: CmdOpSubst,
Num: num,
}, true
case "S":
return Cmd{
Kind: CmdOpSubstLine,
Num: num,
}, true
}
return Cmd{}, false
}
func (p *Parser) ParseEdit(num int, op string, noSubnum bool, subnum int, mv string) (Cmd, bool) {
switch op {
case "J":
return Cmd{
Kind: CmdEditJoin,
Num: num,
}, true
case ">>":
return Cmd{
Kind: CmdEditIndent,
Num: num,
}, true
case "<<":
return Cmd{
Kind: CmdEditOutdent,
Num: num,
}, true
case ">":
/*
return Cmd{
Kind: CmdEditIndentRegion,
Start: Loc{}, // TODO
End: Loc{}, // TODO
}, true
*/
case "<":
/*
return Cmd{
Kind: CmdEditOutdentRegion,
Start: Loc{}, // TODO
End: Loc{}, // TODO
}, true
*/
}
return Cmd{}, false
}
var compoundSet = map[rune]struct{}{
']': {},
'[': {},
'`': {},
'\'': {},
'z': {},
'y': {},
'd': {},
'c': {},
@@ -174,17 +638,15 @@ func (p *Parser) Parse() (Cmd, bool) {
return Cmd{}, false
}
if len(p.buf) == 1 {
if p.buf[0] == '0' { // special
return Cmd{Kind: CmdMoveToStart}, true
}
if p.buf[0] == '0' { // special
return Cmd{Kind: CmdMoveToStart}, true
}
i := 0
var reg rune = 0
if p.buf[i] == '"' {
if len(p.buf) > i+1 {
reg = p.buf[i+1]
if p.buf[0] == '"' {
if len(p.buf) > 1 {
reg = p.buf[1]
i += 2
}
}
@@ -196,9 +658,9 @@ func (p *Parser) Parse() (Cmd, bool) {
}
i++
}
noNum := i == iPrev
noNum := i <= iPrev
num := 1
if i > 0 {
if i > iPrev {
s := string(p.buf[iPrev:i])
n, err := strconv.Atoi(s)
if err != nil {
@@ -207,6 +669,37 @@ func (p *Parser) Parse() (Cmd, bool) {
num = n
}
if i < len(p.buf) {
var letter rune = 0
_, ok := letterOpSet[p.buf[i]]
if ok {
if i+1 >= len(p.buf) {
return Cmd{}, false
}
op := string(p.buf[i : i+1])
letter = p.buf[i+1]
cmd, ok := p.ParseLetter(num, op, letter)
if ok {
return cmd, true
}
}
_, ok = letterMoveSet[p.buf[i]]
if ok {
if i+1 >= len(p.buf) {
return Cmd{}, false
}
mv := string(p.buf[i : i+1])
letter = p.buf[i+1]
cmd, ok := p.ParseFind(num, mv, letter)
if ok {
return cmd, true
}
}
if letter != 0 {
return Cmd{Kind: CmdInvalid}, true
}
}
iPrev = i
for i < len(p.buf) {
if p.buf[i] >= '0' && p.buf[i] <= '9' {
@@ -218,31 +711,6 @@ func (p *Parser) Parse() (Cmd, bool) {
return Cmd{}, false
}
if i+1 < len(p.buf) {
var letter rune = 0
_, ok := letterOpSet[p.buf[i]]
if ok {
op := string(p.buf[i : i+1])
letter = p.buf[i+1]
cmd, ok := p.ParseLetter(num, op, letter)
if ok {
return cmd, true
}
}
_, ok = letterMoveSet[p.buf[i]]
if ok {
mv := string(p.buf[i : i+1])
letter = p.buf[i+1]
cmd, ok := p.ParseMove(noNum, num, mv, letter)
if ok {
return cmd, true
}
}
if letter != 0 {
return Cmd{Kind: CmdInvalid}, true
}
}
mv := string(p.buf[iPrev:i])
cmd, ok := p.ParseMove(noNum, num, mv, 0)
@@ -252,11 +720,23 @@ func (p *Parser) Parse() (Cmd, bool) {
op := mv
opFirst := p.buf[iPrev]
cmd, ok = p.ParseView(num, op)
if ok {
return cmd, true
}
pat := ""
if op == "/" || op == "?" {
// TODO input pat
}
cmd, ok = p.ParseSearch(op, pat)
if ok {
return cmd, true
}
cmd, ok = p.ParseInsert(num, op)
if ok {
return cmd, true
}
cmd, ok = p.ParseMisc(op)
cmd, ok = p.ParseMisc(num, op)
if ok {
return cmd, true
}
@@ -268,7 +748,7 @@ func (p *Parser) Parse() (Cmd, bool) {
}
i++
}
noSubnum := i == iPrev
noSubnum := i <= iPrev
subnum := 1
if i > iPrev {
s := string(p.buf[iPrev:i])
@@ -279,20 +759,14 @@ func (p *Parser) Parse() (Cmd, bool) {
subnum = n
}
iPrev = i
for i < len(p.buf) {
if p.buf[i] >= '0' && p.buf[i] <= '9' {
break
}
i++
}
var letter rune = 0
if i+1 < len(p.buf) {
if i < len(p.buf) {
_, ok := letterMoveSet[p.buf[i]]
if ok {
mv = string(p.buf[i : i+1])
letter = p.buf[i+1]
if i+1 < len(p.buf) {
mv = string(p.buf[i : i+1])
letter = p.buf[i+1]
}
}
}
@@ -307,6 +781,10 @@ func (p *Parser) Parse() (Cmd, bool) {
if ok {
return cmd, true
}
cmd, ok = p.ParseEdit(num, op, noSubnum, subnum, mv)
if ok {
return cmd, true
}
if len(op) < 2 {
_, ok := compoundSet[opFirst]

View File

@@ -6,7 +6,9 @@ func (ed *Editor) Run(c Cmd) bool {
ed.Ring("not (yet) a vi command [" + ed.parser.String() + "]")
ed.parser.Clear()
return true
}
switch c.Kind {
case CmdMoveLeft:
ed.MoveLeft(c.Num)
return true
@@ -33,28 +35,305 @@ func (ed *Editor) Run(c Cmd) bool {
ed.MoveToColumn(c.Num)
return true
// TODO
case CmdMoveByWord:
ed.MoveByWord(c.Num)
return true
case CmdMoveBackwardByWord:
ed.MoveBackwardByWord(c.Num)
return true
case CmdMoveToEndOfWord:
ed.MoveToEndOfWord(c.Num)
return true
case CmdMoveByLooseWord:
ed.MoveByLooseWord(c.Num)
return true
case CmdMoveBackwardByLooseWord:
ed.MoveBackwardByLooseWord(c.Num)
return true
case CmdMoveToEndOfLooseWord:
ed.MoveToEndOfLooseWord(c.Num)
return true
case CmdMoveToNonBlankOfNextLine:
ed.MoveToNonBlankOfNextLine(c.Num)
return true
case CmdMoveToNonBlankOfPrevLine:
ed.MoveToNonBlankOfPrevLine(c.Num)
return true
case CmdMoveToLastLine:
ed.MoveToLastLine()
return true
case CmdMoveToLine:
ed.MoveToLine(c.Num)
return true
case CmdMoveBySentence:
ed.MoveBySentence(c.Num)
return true
case CmdMoveBackwardBySentence:
ed.MoveBackwardBySentence(c.Num)
return true
case CmdMoveByParagraph:
ed.MoveByParagraph(c.Num)
return true
case CmdMoveBackwardByParagraph:
ed.MoveBackwardByParagraph(c.Num)
return true
case CmdMoveBySection:
ed.MoveBySection(c.Num)
return true
case CmdMoveBackwardBySection:
ed.MoveBackwardBySection(c.Num)
return true
case CmdMoveToTopOfView:
ed.MoveToTopOfView()
return true
case CmdMoveToMiddleOfView:
ed.MoveToMiddleOfView()
return true
case CmdMoveToBottomOfView:
ed.MoveToBottomOfView()
return true
case CmdMoveToBelowTopOfView:
ed.MoveToBelowTopOfView(c.Num)
return true
case CmdMoveToAboveBottomOfView:
ed.MoveToAboveBottomOfView(c.Num)
return true
}
switch c.Kind {
case CmdMarkSet:
ed.MarkSet(c.Letter)
return true
case CmdMarkMoveTo:
ed.MarkMoveTo(c.Letter)
return true
case CmdMarkMoveToLine:
ed.MarkMoveToLine(c.Letter)
return true
case CmdMarkBack:
ed.MarkBack()
return true
case CmdMarkBackToLine:
ed.MarkBackToLine()
return true
}
switch c.Kind {
case CmdViewDown:
ed.ViewDown(c.Num)
return true
case CmdViewUp:
ed.ViewUp(c.Num)
return true
case CmdViewDownHalf:
ed.ViewDownHalf(c.Num)
return true
case CmdViewUpHalf:
ed.ViewUpHalf(c.Num)
return true
case CmdViewDownLine:
ed.ViewDownLine(c.Num)
return true
case CmdViewUpLine:
ed.ViewUpLine(c.Num)
return true
case CmdViewToTop:
ed.ViewToTop()
return true
case CmdViewToMiddle:
ed.ViewToMiddle()
return true
case CmdViewToBottom:
ed.ViewToBottom()
return true
case CmdViewRedraw:
ed.ViewRedraw()
return true
}
switch c.Kind {
case CmdSearchForward:
ed.SearchForward(c.Pat)
return true
case CmdSearchBackward:
ed.SearchBackward(c.Pat)
return true
case CmdSearchNextMatch:
ed.SearchNextMatch()
return true
case CmdSearchPrevMatch:
ed.SearchPrevMatch()
return true
case CmdSearchRepeatForward:
ed.SearchRepeatForward()
return true
case CmdSearchRepeatBackward:
ed.SearchRepeatBackward()
return true
}
switch c.Kind {
case CmdFindForward:
ed.FindForward(c.Letter, c.Num)
return true
case CmdFindBackward:
ed.FindBackward(c.Letter, c.Num)
return true
case CmdFindBeforeForward:
ed.FindBeforeForward(c.Letter, c.Num)
return true
case CmdFindBeforeBackward:
ed.FindBeforeBackward(c.Letter, c.Num)
return true
case CmdFindNextMatch:
ed.FindNextMatch(c.Num)
return true
case CmdFindPrevMatch:
ed.FindPrevMatch(c.Num)
return true
}
switch c.Kind {
case CmdInsertBefore:
ed.InsertBefore(c.Num)
return true
case CmdInsertAfter:
ed.InsertAfter(c.Num)
return true
case CmdInsertBeforeNonBlank:
ed.InsertBeforeNonBlank(c.Num)
return true
case CmdInsertAfterEnd:
ed.InsertAfterEnd(c.Num)
return true
case CmdInsertOverwrite:
ed.InsertOverwrite(c.Num)
return true
// TODO
case CmdInsertOpenBelow:
ed.InsertOpenBelow(c.Num)
return true
case CmdInsertOpenAbove:
ed.InsertOpenAbove(c.Num)
return true
case CmdOpCopyLine:
ed.OpCopyLine(c.Num)
return true
case CmdOpCopyRegion:
ed.OpCopyRegion(c.Start, c.End)
return true
case CmdOpCopyLineRegion:
ed.OpCopyLineRegion(c.StartRow, c.EndRow)
return true
case CmdOpCopyWord:
ed.OpCopyWord(c.Num)
return true
case CmdOpCopyToEnd:
ed.OpCopyToEnd(c.Num)
return true
case CmdOpCopyLineIntoReg:
ed.OpCopyLineIntoReg(c.Reg, c.Num)
return true
case CmdOpPaste:
ed.OpPaste(c.Num)
return true
case CmdOpPasteBefore:
ed.OpPasteBefore(c.Num)
return true
case CmdOpPasteFromReg:
ed.OpPasteFromReg(c.Reg, c.Num)
return true
case CmdOpDelete:
ed.OpDelete(c.Num)
return true
case CmdOpDeleteBefore:
ed.OpDeleteBefore(c.Num)
return true
case CmdOpDeleteLine:
ed.OpDeleteLine(c.Num)
return true
case CmdOpDeleteRegion:
ed.OpDeleteRegion(c.Start, c.End)
return true
case CmdOpDeleteLineRegion:
ed.OpDeleteLineRegion(c.StartRow, c.EndRow)
return true
case CmdOpDeleteWord:
ed.OpDeleteWord(c.Num)
return true
case CmdOpDeleteToEnd:
ed.OpDeleteToEnd(c.Num)
return true
// TODO
case CmdOpChangeLine:
ed.OpChangeLine(c.Num)
return true
case CmdOpChangeRegion:
ed.OpChangeRegion(c.Start, c.End)
return true
case CmdOpChangeLineRegion:
ed.OpChangeLineRegion(c.StartRow, c.EndRow)
return true
case CmdOpChangeWord:
ed.OpChangeWord(c.Num)
return true
case CmdOpChangeToEnd:
ed.OpChangeToEnd(c.Num)
return true
case CmdOpSubst:
ed.OpSubst(c.Num)
return true
case CmdOpSubstLine:
ed.OpSubstLine(c.Num)
return true
}
switch c.Kind {
case CmdEditReplace:
ed.EditReplace(c.Letter, c.Num)
return true
case CmdEditJoin:
ed.EditJoin(c.Num)
return true
case CmdEditIndent:
ed.EditIndent(c.Num)
return true
case CmdEditOutdent:
ed.EditOutdent(c.Num)
return true
case CmdEditIndentRegion:
ed.EditIndentRegion(c.Start, c.End)
return true
case CmdEditOutdentRegion:
ed.EditOutdentRegion(c.Start, c.End)
return true
}
switch c.Kind {
case CmdMiscShowInfo:
ed.MiscShowInfo()
return true
case CmdMiscRepeat:
ed.MiscRepeat(c.Num)
return true
case CmdMiscUndo:
ed.MiscUndo(c.Num)
return true
case CmdMiscRestore:
ed.MiscRestore()
return true
case CmdMiscSaveAndQuit:
ed.MiscSaveAndQuit()
return true
// TODO
}
return false

41
internal/editor/search.go Normal file
View File

@@ -0,0 +1,41 @@
package editor
/////////////////////
// Search Commands //
/////////////////////
// /<pattern> Enter : Search <pattern> forward.
func (ed *Editor) SearchForward(s string) {
ed.EnsureCommand()
ed.Unimplemented("SearchForward")
}
// ?<pattern> Enter : Search <pattern> backward.
func (ed *Editor) SearchBackward(s string) {
ed.EnsureCommand()
ed.Unimplemented("SearchBackward")
}
// n : Search next match.
func (ed *Editor) SearchNextMatch() {
ed.EnsureCommand()
ed.Unimplemented("SearchNextMatch")
}
// N : Search previous match.
func (ed *Editor) SearchPrevMatch() {
ed.EnsureCommand()
ed.Unimplemented("SearchPrevMatch")
}
// / Enter : Repeat last search forward.
func (ed *Editor) SearchRepeatForward() {
ed.EnsureCommand()
ed.Unimplemented("SearchRepeatForward")
}
// ? Enter : Repeat last search backward.
func (ed *Editor) SearchRepeatBackward() {
ed.EnsureCommand()
ed.Unimplemented("SearchRepeatBackward")
}

View File

@@ -1,110 +1,77 @@
package editor
import (
"unicode/utf8"
///////////////////
// View Commands //
///////////////////
"tea.kareha.org/lab/termi"
)
//
// Scroll by View Height / Scroll by Line
//
func (ed *Editor) LineHeight(line string) int {
rc := utf8.RuneCountInString(line)
width := termi.StringWidth(line, rc)
return 1 + max(width-1, 0)/ed.w
// Ctrl-f : Scroll down by view height.
func (ed *Editor) ViewDown(n int) {
ed.EnsureCommand()
ed.Unimplemented("ViewDown")
}
func (ed *Editor) DrawBuffer() {
y := 0
for i := ed.vrow; i < len(ed.lines); i++ {
line := ed.Line(i)
termi.MoveCursor(0, y)
termi.Draw(line)
y += ed.LineHeight(line)
if y >= ed.h-1 {
break
}
}
for ; y < ed.h-1; y++ {
termi.MoveCursor(0, y)
termi.Draw("~")
}
// Ctrl-b : Scroll up by view height.
func (ed *Editor) ViewUp(n int) {
ed.EnsureCommand()
ed.Unimplemented("ViewUp")
}
func (ed *Editor) DrawStatus() {
var m string
switch ed.mode {
case ModeCommand:
m = "vi command"
case ModeInsert:
m = "vi insert"
case ModeSearch:
m = "vi search"
case ModePrompt:
m = "vi prompt"
default:
panic("invalid mode")
}
termi.MoveCursor(0, ed.h-1)
if ed.message != "" {
termi.EnableInvert()
termi.Print(ed.message)
termi.DisableInvert()
ed.message = ""
} else {
termi.Printf("[%s] %s %d,%d %s", ed.parser.Cache(), m, ed.row, ed.col, ed.path)
}
// Ctrl-d : Scroll down by half view height.
func (ed *Editor) ViewDownHalf(n int) {
ed.EnsureCommand()
ed.Unimplemented("ViewDownHalf")
}
func (ed *Editor) UpdateCursor() {
// XXX approximation
width := termi.StringWidth(ed.CurrentLine(), ed.col)
ed.x = width % ed.w
dy := width / ed.w
if ed.row < ed.vrow {
ed.vrow = ed.row
}
y := 0
for i := ed.vrow; i < ed.row; i++ {
y += ed.LineHeight(ed.lines[i])
}
ed.y = y + dy
for ed.y >= ed.h-1 {
ed.vrow++
y := 0
for i := ed.vrow; i < ed.row; i++ {
y += ed.LineHeight(ed.lines[i])
}
ed.y = y + dy
}
// Ctrl-u : Scroll up by half view height.
func (ed *Editor) ViewUpHalf(n int) {
ed.EnsureCommand()
ed.Unimplemented("ViewUpHalf")
}
func (ed *Editor) Repaint() {
w, h := termi.Size()
ed.w = w
ed.h = h
termi.HideCursor()
termi.Clear()
termi.HomeCursor()
ed.UpdateCursor()
ed.DrawBuffer()
ed.DrawStatus()
termi.MoveCursor(ed.x, ed.y)
termi.ShowCursor()
// Ctrl-y : Scroll down by line.
func (ed *Editor) ViewDownLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("ViewDownLine")
}
func (ed *Editor) Draw() {
ed.Repaint()
// Ctrl-e : Scroll up by line.
func (ed *Editor) ViewUpLine(n int) {
ed.EnsureCommand()
ed.Unimplemented("ViewUpLine")
}
//
// Reposition
//
// z Enter : Reposition cursor line to top of view.
func (ed *Editor) ViewToTop() {
ed.EnsureCommand()
ed.Unimplemented("ViewToTop")
}
// z. : Reposition cursor line middle of view.
func (ed *Editor) ViewToMiddle() {
ed.EnsureCommand()
ed.Unimplemented("ViewToMiddle")
}
// z- : Reposition cursor line bottom of view.
func (ed *Editor) ViewToBottom() {
ed.EnsureCommand()
ed.Unimplemented("ViewToBottom")
}
//
// Redraw
//
// Ctrl-l : Redraw view.
func (ed *Editor) ViewRedraw() {
ed.EnsureCommand()
ed.Unimplemented("ViewRedraw")
}