diff --git a/evaldo/builtins.go b/evaldo/builtins.go index 4a6d9aa4..8310fe38 100644 --- a/evaldo/builtins.go +++ b/evaldo/builtins.go @@ -1262,7 +1262,7 @@ var builtins = map[string]*env.Builtin{ }, "_/": { // ** Argsn: 2, - Doc: "Divided two numbers.", + Doc: "Divide two numbers and return a decimal.", Pure: true, Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { switch a := arg0.(type) { @@ -1305,6 +1305,51 @@ var builtins = map[string]*env.Builtin{ } }, }, + "_//": { // ** + Argsn: 2, + Doc: "Divides two numbers and return truncated integer.", + Pure: true, + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + switch a := arg0.(type) { + case env.Integer: + switch b := arg1.(type) { + case env.Integer: + if b.Value == 0 { + ps.FailureFlag = true + return MakeBuiltinError(ps, "Can't divide by Zero.", "_/") + } + return *env.NewInteger(a.Value / b.Value) + case env.Decimal: + if b.Value == 0.0 { + ps.FailureFlag = true + return MakeBuiltinError(ps, "Can't divide by Zero.", "_/") + } + return *env.NewInteger(a.Value / int64(b.Value)) + default: + return MakeArgError(ps, 2, []env.Type{env.IntegerType, env.DecimalType}, "_/") + } + case env.Decimal: + switch b := arg1.(type) { + case env.Integer: + if b.Value == 0 { + ps.FailureFlag = true + return MakeBuiltinError(ps, "Can't divide by Zero.", "_/") + } + return *env.NewInteger(int64(a.Value) / b.Value) + case env.Decimal: + if b.Value == 0.0 { + ps.FailureFlag = true + return MakeBuiltinError(ps, "Can't divide by Zero.", "_/") + } + return *env.NewInteger(int64(a.Value) / int64(b.Value)) + default: + return MakeArgError(ps, 2, []env.Type{env.IntegerType, env.DecimalType}, "_/") + } + default: + return MakeArgError(ps, 1, []env.Type{env.IntegerType, env.DecimalType}, "_/") + } + }, + }, "_=": { // *** Argsn: 2, Doc: "Checks if two Rye values are equal.", @@ -7842,7 +7887,7 @@ func registerBuiltin(ps *env.ProgramState, word string, builtin env.Builtin) { // in future a map will probably not be a map but an array and builtin will also support the Kind value idxk := 0 - if strings.Index(word, "//") > 0 { + if word != "_//" && strings.Index(word, "//") > 0 { temp := strings.Split(word, "//") word = temp[1] idxk = ps.Idx.IndexWord(temp[0]) diff --git a/evaldo/repl.go b/evaldo/repl.go index c2981cb0..88a44736 100644 --- a/evaldo/repl.go +++ b/evaldo/repl.go @@ -320,7 +320,6 @@ func DoRyeRepl(es *env.ProgramState, dialect string, showResults bool) { // here fmt.Println(err) return } - defer keyboard.Close() c := make(chan util.KeyEvent) r := Repl{ @@ -332,31 +331,52 @@ func DoRyeRepl(es *env.ProgramState, dialect string, showResults bool) { // here ml := util.NewMicroLiner(c, r.recieveMessage, r.recieveLine) r.ml = ml - ctx := context.Background() - defer ctx.Done() + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + // ctx := context.Background() + // defer os.Exit(0) + // defer ctx.Done() + defer keyboard.Close() go func(ctx context.Context) { for { select { case <-ctx.Done(): + // fmt.Println("Done") return default: + // fmt.Println("Select default") r, k, keyErr := keyboard.GetKey() if err != nil { fmt.Println(keyErr) break } if k == keyboard.KeyCtrlC { - ctx.Done() + // fmt.Println("Ctrl C 1") + cancel() + err1 := util.KillProcess(os.Getpid()) + // err1 := syscall.Kill(os.Getpid(), syscall.SIGINT) + if err1 != nil { + fmt.Println(err.Error()) // TODO -- temprorary just printed + } + //ctx.Done() + // fmt.Println("") + // return + //break + // os.Exit(0) } c <- constructKeyEvent(r, k) } } }(ctx) - _, err = ml.MicroPrompt("x> ", "", 0) + // fmt.Println("MICRO") + _, err = ml.MicroPrompt("x> ", "", 0, ctx) if err != nil { fmt.Println(err) } + // fmt.Println("END") } /* THIS WAS DISABLED TEMP FOR WASM MODE .. 20250116 func DoGeneralInput(es *env.ProgramState, prompt string) { diff --git a/loader/loader.go b/loader/loader.go index dc5613f4..72f61aa0 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -345,7 +345,7 @@ func parseOpword(v *Values, d Any) (Any, error) { word := v.Token() force := 0 var idx int - if len(word) == 1 || word == "<<" || word == "<-" || word == "<~" || word == ">=" || word == "<=" { + if len(word) == 1 || word == "<<" || word == "<-" || word == "<~" || word == ">=" || word == "<=" || word == "//" { // onecharopwords < > + * ... their naming is equal to _< _> _* ... idx = wordIndex.IndexWord("_" + word) } else { @@ -467,7 +467,7 @@ func newParser() *Parser { // TODO -- add string eaddress path url time ONECHARWORDS <- < [<>*+-=/] > NORMOPWORDS <- < ("_"[<>*+-=/]) > PIPEARROWS <- ">>" / "~>" / "->" - OPARROWS <- "<<" / "<~" / "<-" / ">=" / "<=" + OPARROWS <- "<<" / "<~" / "<-" / ">=" / "<=" / "//" LETTER <- < [a-zA-Z^(` + "`" + `] > LETTERORNUM <- < [a-zA-Z0-9-?=.\\!_+<>\]*()] > LETTERORNUMNOX <- < [a-zA-Z0-9-?=.\\!_+\]*()] > diff --git a/main_wasm.go b/main_wasm.go index fd9b0016..f59f188c 100755 --- a/main_wasm.go +++ b/main_wasm.go @@ -4,6 +4,7 @@ package main import ( + "context" "fmt" "regexp" "strings" @@ -99,7 +100,9 @@ func main() { jsCallback = js.Global().Get("receiveMessageFromGo") jsCallback2 = js.Global().Get("receiveLineFromGo") - ml.MicroPrompt("x> ", "", 0) + ctx := context.Background() + + ml.MicroPrompt("x> ", "", 0, ctx) /* for { key := <-c diff --git a/runner/runner.go b/runner/runner.go index ac02de4b..7607a9de 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -542,6 +542,18 @@ func main_rye_repl(_ io.Reader, _ io.Writer, subc bool, here bool, lang string, } } + // c := make(chan os.Signal, 1) + // signal.Notify(c, os.Interrupt, syscall.SIGTERM) + + //go func() { + // sig := <-c + // fmt.Println() + // fmt.Println("Captured signal:", sig) + // Perform cleanup or other actions here + // os.Exit(0) + //}() + //fmt.Println("Waiting for signal") + evaldo.DoRyeRepl(es, lang, evaldo.ShowResults) } diff --git a/tests/basics.rye b/tests/basics.rye index cecff73d..c0798169 100644 --- a/tests/basics.rye +++ b/tests/basics.rye @@ -200,14 +200,23 @@ section "Working with numbers" ; TODO } - ` - group "+-*/" + + group "+-*/ //" "" { { integer decimal } { integer decimal } } { - } + equal { 5 / 2 } 2.5 + equal { 5.0 / 2 } 2.5 + equal { 5 / 2.0 } 2.5 + equal { 5.0 / 2.0 } 2.5 + equal { 5 // 2 } 2 + equal { 5.0 // 2 } 2 + equal { 5 // 2.0 } 2 + equal { 5.0 // 2.0 } 2 + } + ` group "!=><+" "" { { integer decimal } { integer decimal } } diff --git a/util/microliner.go b/util/microliner.go index fa081cf6..dce127b5 100644 --- a/util/microliner.go +++ b/util/microliner.go @@ -2,6 +2,7 @@ package util import ( "bytes" + "context" "fmt" "strconv" "strings" @@ -455,7 +456,7 @@ func (s *MLState) refreshSingleLine(prompt []rune, buf []rune, pos int) error { } // signals end-of-file by pressing Ctrl-D. -func (s *MLState) MicroPrompt(prompt string, text string, pos int) (string, error) { +func (s *MLState) MicroPrompt(prompt string, text string, pos int, ctx1 context.Context) (string, error) { // history related historyEnd := "" var historyPrefix []string @@ -542,623 +543,634 @@ startOfHere: // mainLoop: for { - trace("POS: ") - trace(pos) - // receive next character from channel - next := <-s.next - // s.sendBack(next) - // err := nil - // LBL haveNext: - /* if err != nil { - if s.shouldRestart != nil && s.shouldRestart(err) { - goto restart - } - return "", err - }*/ - - // historyAction = false - //switch v := next.(type) { - //case string: - //} - /* if pos == len(line) && !s.multiLineMode && - len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines - countGlyphs(p)+countGlyphs(line) < s.columns-1 {*/ - ///// pLen := countGlyphs(p) - if next.Ctrl { - switch strings.ToLower(next.Key) { - case "c": - return "", nil - case "a": - pos = 0 - // s.needRefresh = true - case "e": - pos = len(line) - // s.needRefresh = true - case "b": - if pos > 0 { - pos -= len(getSuffixGlyphs(line[:pos], 1)) - //s.needRefresh = true - } else { - s.doBeep() - } - case "f": // right - if pos < len(line) { - pos += len(getPrefixGlyphs(line[pos:], 1)) - // s.needRefresh = true - } else { - s.doBeep() - } - case "k": // delete remainder of line - if pos >= len(line) { - // s.doBeep() - } else { - // if killAction > 0 { - // s.addToKillRing(line[pos:], 1) // Add in apend mode - // } else { - // s.addToKillRing(line[pos:], 0) // Add in normal mode - // } - // killAction = 2 // Mark that there was a kill action - line = line[:pos] - s.needRefresh = true + select { + case <-ctx1.Done(): + // fmt.Println("Exitin due to coancelation") + return "", nil + default: + trace("POS: ") + trace(pos) + // receive next character from channel + next := <-s.next + // s.sendBack(next) + // err := nil + // LBL haveNext: + /* if err != nil { + if s.shouldRestart != nil && s.shouldRestart(err) { + goto restart } - case "l": - s.eraseScreen() - s.needRefresh = true - case "u": // delete to beginning of line - if pos == 0 { - s.doBeep() - } else { - line = line[pos:] + return "", err + }*/ + + // historyAction = false + //switch v := next.(type) { + //case string: + //} + /* if pos == len(line) && !s.multiLineMode && + len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines + countGlyphs(p)+countGlyphs(line) < s.columns-1 {*/ + ///// pLen := countGlyphs(p) + if next.Ctrl { + switch strings.ToLower(next.Key) { + case "c": + /* return "", ErrPromptAborted + line = line[:0] + pos = 0 + s.restartPrompt() */ + // fmt.Print("case C") + return "", nil + case "a": pos = 0 + // s.needRefresh = true + case "e": + pos = len(line) + // s.needRefresh = true + case "b": + if pos > 0 { + pos -= len(getSuffixGlyphs(line[:pos], 1)) + //s.needRefresh = true + } else { + s.doBeep() + } + case "f": // right + if pos < len(line) { + pos += len(getPrefixGlyphs(line[pos:], 1)) + // s.needRefresh = true + } else { + s.doBeep() + } + case "k": // delete remainder of line + if pos >= len(line) { + // s.doBeep() + } else { + // if killAction > 0 { + // s.addToKillRing(line[pos:], 1) // Add in apend mode + // } else { + // s.addToKillRing(line[pos:], 0) // Add in normal mode + // } + // killAction = 2 // Mark that there was a kill action + line = line[:pos] + s.needRefresh = true + } + case "l": + s.eraseScreen() s.needRefresh = true + case "u": // delete to beginning of line + if pos == 0 { + s.doBeep() + } else { + line = line[pos:] + pos = 0 + s.needRefresh = true + } + case "n": + histNext() + case "p": + histPrev() } - case "n": - histNext() - case "p": - histPrev() - } - } else if next.Alt { - switch strings.ToLower(next.Key) { - case "b": - if pos > 0 { - var spaceHere, spaceLeft, leftKnown bool - for { - pos-- - if pos == 0 { - break - } - if leftKnown { - spaceHere = spaceLeft - } else { - spaceHere = unicode.IsSpace(line[pos]) + } else if next.Alt { + switch strings.ToLower(next.Key) { + case "b": + if pos > 0 { + var spaceHere, spaceLeft, leftKnown bool + for { + pos-- + if pos == 0 { + break + } + if leftKnown { + spaceHere = spaceLeft + } else { + spaceHere = unicode.IsSpace(line[pos]) + } + spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true + if !spaceHere && spaceLeft { + break + } } - spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true - if !spaceHere && spaceLeft { - break + } else { + s.doBeep() + } + case "f": + if pos < len(line) { + var spaceHere, spaceLeft, hereKnown bool + for { + pos++ + if pos == len(line) { + break + } + if hereKnown { + spaceLeft = spaceHere + } else { + spaceLeft = unicode.IsSpace(line[pos-1]) + } + spaceHere, hereKnown = unicode.IsSpace(line[pos]), true + if spaceHere && !spaceLeft { + break + } } + } else { + s.doBeep() } - } else { - s.doBeep() - } - case "f": - if pos < len(line) { - var spaceHere, spaceLeft, hereKnown bool + case "d": // Delete next word + if pos == len(line) { + s.doBeep() + break + } + // Remove whitespace to the right + var buf []rune // Store the deleted chars in a buffer for { - pos++ - if pos == len(line) { + if pos == len(line) || !unicode.IsSpace(line[pos]) { break } - if hereKnown { - spaceLeft = spaceHere - } else { - spaceLeft = unicode.IsSpace(line[pos-1]) - } - spaceHere, hereKnown = unicode.IsSpace(line[pos]), true - if spaceHere && !spaceLeft { + buf = append(buf, line[pos]) + line = append(line[:pos], line[pos+1:]...) + } + // Remove non-whitespace to the right + for { + if pos == len(line) || unicode.IsSpace(line[pos]) { break } + buf = append(buf, line[pos]) + line = append(line[:pos], line[pos+1:]...) + trace(buf) } - } else { - s.doBeep() - } - case "d": // Delete next word - if pos == len(line) { - s.doBeep() - break - } - // Remove whitespace to the right - var buf []rune // Store the deleted chars in a buffer - for { - if pos == len(line) || !unicode.IsSpace(line[pos]) { - break - } - buf = append(buf, line[pos]) - line = append(line[:pos], line[pos+1:]...) + s.needRefresh = true + // Save the result on the killRing + /*if killAction > 0 { + s.addToKillRing(buf, 2) // Add in prepend mode + } else { + s.addToKillRing(buf, 0) // Add in normal mode + } */ + // killAction = 2 // Mark that there was some killing + // case "bs": // Erase word + // pos, line, killAction = s.eraseWord(pos, line, killAction) } - // Remove non-whitespace to the right - for { - if pos == len(line) || unicode.IsSpace(line[pos]) { - break + } else { + switch next.Code { + case 13: // Enter + historyStale = true + s.lastLineString = false + // trace2("NL") + if len(line) > 0 && unicode.IsSpace(line[len(line)-1]) { + s.sendBack(fmt.Sprintf("%s⏎\n\r%s", color_emph, reset)) + if s.inString { + s.lastLineString = true + } + } else { + s.sendBack("\n\r") } - buf = append(buf, line[pos]) - line = append(line[:pos], line[pos+1:]...) - trace(buf) - } - s.needRefresh = true - // Save the result on the killRing - /*if killAction > 0 { - s.addToKillRing(buf, 2) // Add in prepend mode - } else { - s.addToKillRing(buf, 0) // Add in normal mode - } */ - // killAction = 2 // Mark that there was some killing - // case "bs": // Erase word - // pos, line, killAction = s.eraseWord(pos, line, killAction) - } - } else { - switch next.Code { - case 13: // Enter - historyStale = true - s.lastLineString = false - // trace2("NL") - if len(line) > 0 && unicode.IsSpace(line[len(line)-1]) { - s.sendBack(fmt.Sprintf("%s⏎\n\r%s", color_emph, reset)) - if s.inString { - s.lastLineString = true + xx := s.enterLine(string(line)) + pos = 0 + if xx == "next line" { + multiline = true + } else { + s.sendBack("\n\r") } - } else { - s.sendBack("\n\r") - } - xx := s.enterLine(string(line)) - pos = 0 - if xx == "next line" { - multiline = true - } else { - s.sendBack("\n\r") - } - line = make([]rune, 0) - trace(line) - goto startOfHere - case 8: // Backspace - if pos <= 0 { - s.doBeep() - } else { - // pos += 1 - n := len(getSuffixGlyphs(line[:pos], 1)) - trace("<---line--->") - trace(line[:pos-n]) - trace(line[pos:]) - trace(n) - trace(pos) + line = make([]rune, 0) trace(line) - // line = append(line[:pos-n], ' ') - line = append(line[:pos-n], line[pos:]...) - // line = line[:pos-1] - trace(line) - // line = append(line[:pos-n], line[pos:]...) - pos -= n - s.needRefresh = true - } - case 46: // Del - if pos >= len(line) { - s.doBeep() - } else { - n := len(getPrefixGlyphs(line[pos:], 1)) - line = append(line[:pos], line[pos+n:]...) - s.needRefresh = true - } - case 39: // Right - if pos < len(line) { - pos += len(getPrefixGlyphs(line[pos:], 1)) - } else { - s.doBeep() - } - case 37: // Left - if pos > 0 { - pos -= len(getSuffixGlyphs(line[:pos], 1)) - traceTop(pos, 3) - } else { - s.doBeep() - } - case 38: // Up - histPrev() - case 40: // Down - histNext() - case 36: // Home - pos = 0 - case 35: // End - pos = len(line) - default: - trace("***************************** ALARM *******************") - vs := []rune(next.Key) - v := vs[0] - - if pos >= countGlyphs(p)+countGlyphs(line) { - line = append(line, v) - //s.sendBack(fmt.Sprintf("%c", v)) - s.needRefresh = true // JM --- - pos++ - } else { - line = append(line[:pos], append([]rune{v}, line[pos:]...)...) - pos++ - s.needRefresh = true + goto startOfHere + case 8: // Backspace + if pos <= 0 { + s.doBeep() + } else { + // pos += 1 + n := len(getSuffixGlyphs(line[:pos], 1)) + trace("<---line--->") + trace(line[:pos-n]) + trace(line[pos:]) + trace(n) + trace(pos) + trace(line) + // line = append(line[:pos-n], ' ') + line = append(line[:pos-n], line[pos:]...) + // line = line[:pos-1] + trace(line) + // line = append(line[:pos-n], line[pos:]...) + pos -= n + s.needRefresh = true + } + case 46: // Del + if pos >= len(line) { + s.doBeep() + } else { + n := len(getPrefixGlyphs(line[pos:], 1)) + line = append(line[:pos], line[pos+n:]...) + s.needRefresh = true + } + case 39: // Right + if pos < len(line) { + pos += len(getPrefixGlyphs(line[pos:], 1)) + } else { + s.doBeep() + } + case 37: // Left + if pos > 0 { + pos -= len(getSuffixGlyphs(line[:pos], 1)) + traceTop(pos, 3) + } else { + s.doBeep() + } + case 38: // Up + histPrev() + case 40: // Down + histNext() + case 36: // Home + pos = 0 + case 35: // End + pos = len(line) + default: + trace("***************************** ALARM *******************") + vs := []rune(next.Key) + v := vs[0] + + if pos >= countGlyphs(p)+countGlyphs(line) { + line = append(line, v) + //s.sendBack(fmt.Sprintf("%c", v)) + s.needRefresh = true // JM --- + pos++ + } else { + line = append(line[:pos], append([]rune{v}, line[pos:]...)...) + pos++ + s.needRefresh = true + } } } - } - - /* } else { - line = append(line[:pos], append([]rune{v}, line[pos:]...)...) - pos++ - s.needRefresh = true - } */ - /* case rune: - switch v { - case cr, lf: - if s.needRefresh { - err := s.refresh(p, line, pos) - if err != nil { - return "", err - } - } - if s.multiLineMode { - s.resetMultiLine(p, line, pos) - } - trace() - break mainLoop - case ctrlA: // Start of line - pos = 0 - s.needRefresh = true - case ctrlE: // End of line - pos = len(line) + /* } else { + line = append(line[:pos], append([]rune{v}, line[pos:]...)...) + pos++ s.needRefresh = true - case ctrlB: // left - if pos > 0 { - pos -= len(getSuffixGlyphs(line[:pos], 1)) + } */ + + /* case rune: + switch v { + case cr, lf: + if s.needRefresh { + err := s.refresh(p, line, pos) + if err != nil { + return "", err + } + } + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } + trace() + break mainLoop + case ctrlA: // Start of line + pos = 0 s.needRefresh = true - } else { - s.doBeep() - } - case ctrlF: // right - if pos < len(line) { - pos += len(getPrefixGlyphs(line[pos:], 1)) + case ctrlE: // End of line + pos = len(line) s.needRefresh = true - } else { - s.doBeep() - } - case ctrlD: // del - if pos == 0 && len(line) == 0 { - // exit - return "", io.EOF - } + case ctrlB: // left + if pos > 0 { + pos -= len(getSuffixGlyphs(line[:pos], 1)) + s.needRefresh = true + } else { + s.doBeep() + } + case ctrlF: // right + if pos < len(line) { + pos += len(getPrefixGlyphs(line[pos:], 1)) + s.needRefresh = true + } else { + s.doBeep() + } + case ctrlD: // del + if pos == 0 && len(line) == 0 { + // exit + return "", io.EOF + } - // ctrlD is a potential EOF, so the rune reader shuts down. - // Therefore, if it isn't actually an EOF, we must re-startPrompt. - s.restartPrompt() + // ctrlD is a potential EOF, so the rune reader shuts down. + // Therefore, if it isn't actually an EOF, we must re-startPrompt. + s.restartPrompt() - if pos >= len(line) { - s.doBeep() - } else { - n := len(getPrefixGlyphs(line[pos:], 1)) - line = append(line[:pos], line[pos+n:]...) - s.needRefresh = true - } - case ctrlK: // delete remainder of line - if pos >= len(line) { - s.doBeep() - } else { - if killAction > 0 { - s.addToKillRing(line[pos:], 1) // Add in apend mode + if pos >= len(line) { + s.doBeep() } else { - s.addToKillRing(line[pos:], 0) // Add in normal mode + n := len(getPrefixGlyphs(line[pos:], 1)) + line = append(line[:pos], line[pos+n:]...) + s.needRefresh = true } + case ctrlK: // delete remainder of line + if pos >= len(line) { + s.doBeep() + } else { + if killAction > 0 { + s.addToKillRing(line[pos:], 1) // Add in apend mode + } else { + s.addToKillRing(line[pos:], 0) // Add in normal mode + } - killAction = 2 // Mark that there was a kill action - line = line[:pos] - s.needRefresh = true - } - case ctrlP: // up - historyAction = true - if historyStale { - historyPrefix = s.getHistoryByPrefix(string(line)) - historyPos = len(historyPrefix) - historyStale = false - } - if historyPos > 0 { - if historyPos == len(historyPrefix) { - historyEnd = string(line) + killAction = 2 // Mark that there was a kill action + line = line[:pos] + s.needRefresh = true } - historyPos-- - line = []rune(historyPrefix[historyPos]) - pos = len(line) - s.needRefresh = true - } else { - s.doBeep() - } - case ctrlN: // down - historyAction = true - if historyStale { - historyPrefix = s.getHistoryByPrefix(string(line)) - historyPos = len(historyPrefix) - historyStale = false - } - if historyPos < len(historyPrefix) { - historyPos++ - if historyPos == len(historyPrefix) { - line = []rune(historyEnd) - } else { + case ctrlP: // up + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos > 0 { + if historyPos == len(historyPrefix) { + historyEnd = string(line) + } + historyPos-- line = []rune(historyPrefix[historyPos]) + pos = len(line) + s.needRefresh = true + } else { + s.doBeep() } - pos = len(line) + case ctrlN: // down + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos < len(historyPrefix) { + historyPos++ + if historyPos == len(historyPrefix) { + line = []rune(historyEnd) + } else { + line = []rune(historyPrefix[historyPos]) + } + pos = len(line) + s.needRefresh = true + } else { + s.doBeep() + } + case ctrlT: // transpose prev glyph with glyph under cursor + if len(line) < 2 || pos < 1 { + s.doBeep() + } else { + if pos == len(line) { + pos -= len(getSuffixGlyphs(line, 1)) + } + prev := getSuffixGlyphs(line[:pos], 1) + next := getPrefixGlyphs(line[pos:], 1) + scratch := make([]rune, len(prev)) + copy(scratch, prev) + copy(line[pos-len(prev):], next) + copy(line[pos-len(prev)+len(next):], scratch) + pos += len(next) + s.needRefresh = true + } + case ctrlL: // clear screen + s.eraseScreen() s.needRefresh = true - } else { - s.doBeep() - } - case ctrlT: // transpose prev glyph with glyph under cursor - if len(line) < 2 || pos < 1 { - s.doBeep() - } else { - if pos == len(line) { - pos -= len(getSuffixGlyphs(line, 1)) + case ctrlC: // reset + trace("^C") + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } + if s.ctrlCAborts { + return "", ErrPromptAborted + } + line = line[:0] + pos = 0 + fmt.Print(prompt) + s.restartPrompt() + case ctrlH, bs: // Backspace + if pos <= 0 { + s.doBeep() + } else { + n := len(getSuffixGlyphs(line[:pos], 1)) + line = append(line[:pos-n], line[pos:]...) + pos -= n + s.needRefresh = true + } + case ctrlU: // Erase line before cursor + if killAction > 0 { + s.addToKillRing(line[:pos], 2) // Add in prepend mode + } else { + s.addToKillRing(line[:pos], 0) // Add in normal mode } - prev := getSuffixGlyphs(line[:pos], 1) - next := getPrefixGlyphs(line[pos:], 1) - scratch := make([]rune, len(prev)) - copy(scratch, prev) - copy(line[pos-len(prev):], next) - copy(line[pos-len(prev)+len(next):], scratch) - pos += len(next) + + killAction = 2 // Mark that there was some killing + line = line[pos:] + pos = 0 s.needRefresh = true - } - case ctrlL: // clear screen - s.eraseScreen() - s.needRefresh = true - case ctrlC: // reset - trace("^C") - if s.multiLineMode { - s.resetMultiLine(p, line, pos) - } - if s.ctrlCAborts { - return "", ErrPromptAborted - } - line = line[:0] - pos = 0 - fmt.Print(prompt) - s.restartPrompt() - case ctrlH, bs: // Backspace - if pos <= 0 { - s.doBeep() - } else { - n := len(getSuffixGlyphs(line[:pos], 1)) - line = append(line[:pos-n], line[pos:]...) - pos -= n + case ctrlW: // Erase word + pos, line, killAction = s.eraseWord(pos, line, killAction) + case ctrlY: // Paste from Yank buffer + line, pos, next, err = s.yank(p, line, pos) + goto haveNext + case ctrlR: // Reverse Search + line, pos, next, err = s.reverseISearch(line, pos) s.needRefresh = true - } - case ctrlU: // Erase line before cursor - if killAction > 0 { - s.addToKillRing(line[:pos], 2) // Add in prepend mode - } else { - s.addToKillRing(line[:pos], 0) // Add in normal mode - } - - killAction = 2 // Mark that there was some killing - line = line[pos:] - pos = 0 - s.needRefresh = true - case ctrlW: // Erase word - pos, line, killAction = s.eraseWord(pos, line, killAction) - case ctrlY: // Paste from Yank buffer - line, pos, next, err = s.yank(p, line, pos) - goto haveNext - case ctrlR: // Reverse Search - line, pos, next, err = s.reverseISearch(line, pos) - s.needRefresh = true - goto haveNext - case tab: // Tab completion - line, pos, next, err = s.tabComplete(p, line, pos) - goto haveNext - // Catch keys that do nothing, but you don't want them to beep - case esc: - // DO NOTHING - // Unused keys - case ctrlG: - // JM experimenting 20200108 - //for _, l := range codelines { - // MoveCursorDown(2) - // fmt.Print(l) - // } - //MoveCursorDown(len(codelines)) - return "", ErrJMCodeUp - //line = []rune(codelines[len(codelines)-1]) - //pos = len(line) - s.needRefresh = true - case ctrlS, ctrlO, ctrlQ, ctrlV, ctrlX, ctrlZ: - fallthrough - // Catch unhandled control codes (anything <= 31) - case 0, 28, 29, 30, 31: - s.doBeep() - default: - if pos == len(line) && !s.multiLineMode && - len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines - countGlyphs(p)+countGlyphs(line) < s.columns-1 { - line = append(line, v) - fmt.Printf("%c", v) - s.needRefresh = true // JM --- - pos++ - - } else { - line = append(line[:pos], append([]rune{v}, line[pos:]...)...) - pos++ + goto haveNext + case tab: // Tab completion + line, pos, next, err = s.tabComplete(p, line, pos) + goto haveNext + // Catch keys that do nothing, but you don't want them to beep + case esc: + // DO NOTHING + // Unused keys + case ctrlG: + // JM experimenting 20200108 + //for _, l := range codelines { + // MoveCursorDown(2) + // fmt.Print(l) + // } + //MoveCursorDown(len(codelines)) + return "", ErrJMCodeUp + //line = []rune(codelines[len(codelines)-1]) + //pos = len(line) s.needRefresh = true - } - if s_instr == 2 && string(v) == "\"" { - s_instr = 0 - } - } - case action: - switch v { - case del: - if pos >= len(line) { - s.doBeep() - } else { - n := len(getPrefixGlyphs(line[pos:], 1)) - line = append(line[:pos], line[pos+n:]...) - } - case left: - if pos > 0 { - pos -= len(getSuffixGlyphs(line[:pos], 1)) - } else { + case ctrlS, ctrlO, ctrlQ, ctrlV, ctrlX, ctrlZ: + fallthrough + // Catch unhandled control codes (anything <= 31) + case 0, 28, 29, 30, 31: s.doBeep() - } - case wordLeft, altB: - if pos > 0 { - var spaceHere, spaceLeft, leftKnown bool - for { - pos-- - if pos == 0 { - break - } - if leftKnown { - spaceHere = spaceLeft - } else { - spaceHere = unicode.IsSpace(line[pos]) - } - spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true - if !spaceHere && spaceLeft { - break - } + default: + if pos == len(line) && !s.multiLineMode && + len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines + countGlyphs(p)+countGlyphs(line) < s.columns-1 { + line = append(line, v) + fmt.Printf("%c", v) + s.needRefresh = true // JM --- + pos++ + + } else { + line = append(line[:pos], append([]rune{v}, line[pos:]...)...) + pos++ + s.needRefresh = true + } + if s_instr == 2 && string(v) == "\"" { + s_instr = 0 } - } else { - s.doBeep() - } - case right: - if pos < len(line) { - pos += len(getPrefixGlyphs(line[pos:], 1)) - } else { - s.doBeep() } - case wordRight, altF: - if pos < len(line) { - var spaceHere, spaceLeft, hereKnown bool - for { - pos++ - if pos == len(line) { - break + case action: + switch v { + case del: + if pos >= len(line) { + s.doBeep() + } else { + n := len(getPrefixGlyphs(line[pos:], 1)) + line = append(line[:pos], line[pos+n:]...) + } + case left: + if pos > 0 { + pos -= len(getSuffixGlyphs(line[:pos], 1)) + } else { + s.doBeep() + } + case wordLeft, altB: + if pos > 0 { + var spaceHere, spaceLeft, leftKnown bool + for { + pos-- + if pos == 0 { + break + } + if leftKnown { + spaceHere = spaceLeft + } else { + spaceHere = unicode.IsSpace(line[pos]) + } + spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true + if !spaceHere && spaceLeft { + break + } } - if hereKnown { - spaceLeft = spaceHere - } else { - spaceLeft = unicode.IsSpace(line[pos-1]) + } else { + s.doBeep() + } + case right: + if pos < len(line) { + pos += len(getPrefixGlyphs(line[pos:], 1)) + } else { + s.doBeep() + } + case wordRight, altF: + if pos < len(line) { + var spaceHere, spaceLeft, hereKnown bool + for { + pos++ + if pos == len(line) { + break + } + if hereKnown { + spaceLeft = spaceHere + } else { + spaceLeft = unicode.IsSpace(line[pos-1]) + } + spaceHere, hereKnown = unicode.IsSpace(line[pos]), true + if spaceHere && !spaceLeft { + break + } } - spaceHere, hereKnown = unicode.IsSpace(line[pos]), true - if spaceHere && !spaceLeft { - break + } else { + s.doBeep() + } + case up: + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos > 0 { + if historyPos == len(historyPrefix) { + historyEnd = string(line) } + historyPos-- + line = []rune(historyPrefix[historyPos]) + pos = len(line) + } else { + s.doBeep() } - } else { - s.doBeep() - } - case up: - historyAction = true - if historyStale { - historyPrefix = s.getHistoryByPrefix(string(line)) - historyPos = len(historyPrefix) - historyStale = false - } - if historyPos > 0 { - if historyPos == len(historyPrefix) { - historyEnd = string(line) + case down: + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false } - historyPos-- - line = []rune(historyPrefix[historyPos]) - pos = len(line) - } else { - s.doBeep() - } - case down: - historyAction = true - if historyStale { - historyPrefix = s.getHistoryByPrefix(string(line)) - historyPos = len(historyPrefix) - historyStale = false - } - if historyPos < len(historyPrefix) { - historyPos++ - if historyPos == len(historyPrefix) { - line = []rune(historyEnd) + if historyPos < len(historyPrefix) { + historyPos++ + if historyPos == len(historyPrefix) { + line = []rune(historyEnd) + } else { + line = []rune(historyPrefix[historyPos]) + } + pos = len(line) } else { - line = []rune(historyPrefix[historyPos]) + s.doBeep() } + case home: // Start of line + pos = 0 + case end: // End of line pos = len(line) - } else { - s.doBeep() - } - case home: // Start of line - pos = 0 - case end: // End of line - pos = len(line) - case altD: // Delete next word - if pos == len(line) { - s.doBeep() - break - } - // Remove whitespace to the right - var buf []rune // Store the deleted chars in a buffer - for { - if pos == len(line) || !unicode.IsSpace(line[pos]) { + case altD: // Delete next word + if pos == len(line) { + s.doBeep() break } - buf = append(buf, line[pos]) - line = append(line[:pos], line[pos+1:]...) - } - // Remove non-whitespace to the right - for { - if pos == len(line) || unicode.IsSpace(line[pos]) { - break + // Remove whitespace to the right + var buf []rune // Store the deleted chars in a buffer + for { + if pos == len(line) || !unicode.IsSpace(line[pos]) { + break + } + buf = append(buf, line[pos]) + line = append(line[:pos], line[pos+1:]...) } - buf = append(buf, line[pos]) - line = append(line[:pos], line[pos+1:]...) - } - // Save the result on the killRing - if killAction > 0 { - s.addToKillRing(buf, 2) // Add in prepend mode - } else { - s.addToKillRing(buf, 0) // Add in normal mode - } - killAction = 2 // Mark that there was some killing - case altBs: // Erase word - pos, line, killAction = s.eraseWord(pos, line, killAction) - case winch: // Window change - if s.multiLineMode { - if s.maxRows-s.cursorRows > 0 { - s.moveDown(s.maxRows - s.cursorRows) + // Remove non-whitespace to the right + for { + if pos == len(line) || unicode.IsSpace(line[pos]) { + break + } + buf = append(buf, line[pos]) + line = append(line[:pos], line[pos+1:]...) + } + // Save the result on the killRing + if killAction > 0 { + s.addToKillRing(buf, 2) // Add in prepend mode + } else { + s.addToKillRing(buf, 0) // Add in normal mode } - for i := 0; i < s.maxRows-1; i++ { - s.cursorPos(0) - s.eraseLine() - s.moveUp(1) + killAction = 2 // Mark that there was some killing + case altBs: // Erase word + pos, line, killAction = s.eraseWord(pos, line, killAction) + case winch: // Window change + if s.multiLineMode { + if s.maxRows-s.cursorRows > 0 { + s.moveDown(s.maxRows - s.cursorRows) + } + for i := 0; i < s.maxRows-1; i++ { + s.cursorPos(0) + s.eraseLine() + s.moveUp(1) + } + s.maxRows = 1 + s.cursorRows = 1 } - s.maxRows = 1 - s.cursorRows = 1 } + s.needRefresh = true + } */ + //if true || s.needRefresh { //&& !s.inputWaiting() { + // ALWAYS REFRESH SO WE HAVE JUST ONE TRUTH + err := s.refresh(p, line, pos) + if err != nil { + return "", err } - s.needRefresh = true - } */ - //if true || s.needRefresh { //&& !s.inputWaiting() { - // ALWAYS REFRESH SO WE HAVE JUST ONE TRUTH - err := s.refresh(p, line, pos) - if err != nil { - return "", err - } - // } else { - ///// s.cursorPos(pLen + pos) - // } - /*if !historyAction { - historyStale = true + // } else { + ///// s.cursorPos(pLen + pos) + // } + /*if !historyAction { + historyStale = true + } + if killAction > 0 { + killAction-- + }*/ } - if killAction > 0 { - killAction-- - }*/ } // return string(line), nil } diff --git a/util/os_windows.go b/util/os_windows.go new file mode 100644 index 00000000..32757d10 --- /dev/null +++ b/util/os_windows.go @@ -0,0 +1,15 @@ +//go:build windows +// +build windows + +package util + +import "syscall" + +func KillProcess(pid int) error { + handle, err := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid)) + if err != nil { + return err + } + defer syscall.CloseHandle(handle) + return syscall.TerminateProcess(handle, 0) +} diff --git a/util/os_windows_not.go b/util/os_windows_not.go new file mode 100644 index 00000000..ab320c0d --- /dev/null +++ b/util/os_windows_not.go @@ -0,0 +1,10 @@ +//go:build !windows +// +build !windows + +package util + +import "syscall" + +func KillProcess(pid int) error { + return syscall.Kill(pid, syscall.SIGTERM) +}