+++ /dev/null
-//
-// getline.cs: A command line editor
-//
-// Authors:
-// Miguel de Icaza (miguel@novell.com)
-//
-// Copyright 2008 Novell, Inc.
-//
-// Dual-licensed under the terms of the MIT X11 license or the
-// Apache License 2.0
-//
-// USE -define:DEMO to build this as a standalone file and test it
-//
-// TODO:
-// Enter an error (a = 1); Notice how the prompt is in the wrong line
-// This is caused by Stderr not being tracked by System.Console.
-// Completion support
-// Why is Thread.Interrupt not working? Currently I resort to Abort which is too much.
-//
-// Limitations in System.Console:
-// Console needs SIGWINCH support of some sort
-// Console needs a way of updating its position after things have been written
-// behind its back (P/Invoke puts for example).
-// System.Console needs to get the DELETE character, and report accordingly.
-//
-
-using System;
-using System.Text;
-using System.IO;
-using System.Threading;
-using System.Reflection;
-
-namespace Mono.Terminal {
-
- public class LineEditor {
-
- public class Completion {
- public string [] Result;
- public string Prefix;
-
- public Completion (string prefix, string [] result)
- {
- Prefix = prefix;
- Result = result;
- }
- }
-
- public delegate Completion AutoCompleteHandler (string text, int pos);
-
- //static StreamWriter log;
-
- // The text being edited.
- StringBuilder text;
-
- // The text as it is rendered (replaces (char)1 with ^A on display for example).
- StringBuilder rendered_text;
-
- // The prompt specified, and the prompt shown to the user.
- string prompt;
- string shown_prompt;
-
- // The current cursor position, indexes into "text", for an index
- // into rendered_text, use TextToRenderPos
- int cursor;
-
- // The row where we started displaying data.
- int home_row;
-
- // The maximum length that has been displayed on the screen
- int max_rendered;
-
- // If we are done editing, this breaks the interactive loop
- bool done = false;
-
- // The thread where the Editing started taking place
- Thread edit_thread;
-
- // Our object that tracks history
- History history;
-
- // The contents of the kill buffer (cut/paste in Emacs parlance)
- string kill_buffer = "";
-
- // The string being searched for
- string search;
- string last_search;
-
- // whether we are searching (-1= reverse; 0 = no; 1 = forward)
- int searching;
-
- // The position where we found the match.
- int match_at;
-
- // Used to implement the Kill semantics (multiple Alt-Ds accumulate)
- KeyHandler last_handler;
-
- delegate void KeyHandler ();
-
- struct Handler {
- public ConsoleKeyInfo CKI;
- public KeyHandler KeyHandler;
-
- public Handler (ConsoleKey key, KeyHandler h)
- {
- CKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);
- KeyHandler = h;
- }
-
- public Handler (char c, KeyHandler h)
- {
- KeyHandler = h;
- // Use the "Zoom" as a flag that we only have a character.
- CKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);
- }
-
- public Handler (ConsoleKeyInfo cki, KeyHandler h)
- {
- CKI = cki;
- KeyHandler = h;
- }
-
- public static Handler Control (char c, KeyHandler h)
- {
- return new Handler ((char) (c - 'A' + 1), h);
- }
-
- public static Handler Alt (char c, ConsoleKey k, KeyHandler h)
- {
- ConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);
- return new Handler (cki, h);
- }
- }
-
- /// <summary>
- /// Invoked when the user requests auto-completion using the tab character
- /// </summary>
- /// <remarks>
- /// The result is null for no values found, an array with a single
- /// string, in that case the string should be the text to be inserted
- /// for example if the word at pos is "T", the result for a completion
- /// of "ToString" should be "oString", not "ToString".
- ///
- /// When there are multiple results, the result should be the full
- /// text
- /// </remarks>
- public AutoCompleteHandler AutoCompleteEvent;
-
- static Handler [] handlers;
-
- public LineEditor (string name) : this (name, 10) { }
-
- public LineEditor (string name, int histsize)
- {
- handlers = new Handler [] {
- new Handler (ConsoleKey.Home, CmdHome),
- new Handler (ConsoleKey.End, CmdEnd),
- new Handler (ConsoleKey.LeftArrow, CmdLeft),
- new Handler (ConsoleKey.RightArrow, CmdRight),
- new Handler (ConsoleKey.UpArrow, CmdHistoryPrev),
- new Handler (ConsoleKey.DownArrow, CmdHistoryNext),
- new Handler (ConsoleKey.Enter, CmdDone),
- new Handler (ConsoleKey.Backspace, CmdBackspace),
- new Handler (ConsoleKey.Delete, CmdDeleteChar),
- new Handler (ConsoleKey.Tab, CmdTabOrComplete),
-
- // Emacs keys
- Handler.Control ('A', CmdHome),
- Handler.Control ('E', CmdEnd),
- Handler.Control ('B', CmdLeft),
- Handler.Control ('F', CmdRight),
- Handler.Control ('P', CmdHistoryPrev),
- Handler.Control ('N', CmdHistoryNext),
- Handler.Control ('K', CmdKillToEOF),
- Handler.Control ('Y', CmdYank),
- Handler.Control ('D', CmdDeleteChar),
- Handler.Control ('L', CmdRefresh),
- Handler.Control ('R', CmdReverseSearch),
- Handler.Control ('G', delegate {} ),
- Handler.Alt ('B', ConsoleKey.B, CmdBackwardWord),
- Handler.Alt ('F', ConsoleKey.F, CmdForwardWord),
-
- Handler.Alt ('D', ConsoleKey.D, CmdDeleteWord),
- Handler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),
-
- // DEBUG
- //Handler.Control ('T', CmdDebug),
-
- // quote
- Handler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })
- };
-
- rendered_text = new StringBuilder ();
- text = new StringBuilder ();
-
- history = new History (name, histsize);
-
- //if (File.Exists ("log"))File.Delete ("log");
- //log = File.CreateText ("log");
- }
-
- void CmdDebug ()
- {
- history.Dump ();
- Console.WriteLine ();
- Render ();
- }
-
- void Render ()
- {
- Console.Write (shown_prompt);
- Console.Write (rendered_text);
-
- int max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);
-
- for (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)
- Console.Write (' ');
- max_rendered = shown_prompt.Length + rendered_text.Length;
-
- // Write one more to ensure that we always wrap around properly if we are at the
- // end of a line.
- Console.Write (' ');
-
- UpdateHomeRow (max);
- }
-
- void UpdateHomeRow (int screenpos)
- {
- int lines = 1 + (screenpos / Console.WindowWidth);
-
- home_row = Console.CursorTop - (lines - 1);
- if (home_row < 0)
- home_row = 0;
- }
-
-
- void RenderFrom (int pos)
- {
- int rpos = TextToRenderPos (pos);
- int i;
-
- for (i = rpos; i < rendered_text.Length; i++)
- Console.Write (rendered_text [i]);
-
- if ((shown_prompt.Length + rendered_text.Length) > max_rendered)
- max_rendered = shown_prompt.Length + rendered_text.Length;
- else {
- int max_extra = max_rendered - shown_prompt.Length;
- for (; i < max_extra; i++)
- Console.Write (' ');
- }
- }
-
- void ComputeRendered ()
- {
- rendered_text.Length = 0;
-
- for (int i = 0; i < text.Length; i++){
- int c = (int) text [i];
- if (c < 26){
- if (c == '\t')
- rendered_text.Append (" ");
- else {
- rendered_text.Append ('^');
- rendered_text.Append ((char) (c + (int) 'A' - 1));
- }
- } else
- rendered_text.Append ((char)c);
- }
- }
-
- int TextToRenderPos (int pos)
- {
- int p = 0;
-
- for (int i = 0; i < pos; i++){
- int c;
-
- c = (int) text [i];
-
- if (c < 26){
- if (c == 9)
- p += 4;
- else
- p += 2;
- } else
- p++;
- }
-
- return p;
- }
-
- int TextToScreenPos (int pos)
- {
- return shown_prompt.Length + TextToRenderPos (pos);
- }
-
- string Prompt {
- get { return prompt; }
- set { prompt = value; }
- }
-
- int LineCount {
- get {
- return (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;
- }
- }
-
- void ForceCursor (int newpos)
- {
- cursor = newpos;
-
- int actual_pos = shown_prompt.Length + TextToRenderPos (cursor);
- int row = home_row + (actual_pos/Console.WindowWidth);
- int col = actual_pos % Console.WindowWidth;
-
- if (row >= Console.BufferHeight)
- row = Console.BufferHeight-1;
- Console.SetCursorPosition (col, row);
-
- //log.WriteLine ("Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);
- //log.Flush ();
- }
-
- void UpdateCursor (int newpos)
- {
- if (cursor == newpos)
- return;
-
- ForceCursor (newpos);
- }
-
- void InsertChar (char c)
- {
- int prev_lines = LineCount;
- text = text.Insert (cursor, c);
- ComputeRendered ();
- if (prev_lines != LineCount){
-
- Console.SetCursorPosition (0, home_row);
- Render ();
- ForceCursor (++cursor);
- } else {
- RenderFrom (cursor);
- ForceCursor (++cursor);
- UpdateHomeRow (TextToScreenPos (cursor));
- }
- }
-
- //
- // Commands
- //
- void CmdDone ()
- {
- done = true;
- }
-
- void CmdTabOrComplete ()
- {
- bool complete = false;
-
- if (AutoCompleteEvent != null){
- if (TabAtStartCompletes)
- complete = true;
- else {
- for (int i = 0; i < cursor; i++){
- if (!Char.IsWhiteSpace (text [i])){
- complete = true;
- break;
- }
- }
- }
-
- if (complete){
- Completion completion = AutoCompleteEvent (text.ToString (), cursor);
- string [] completions = completion.Result;
- if (completions == null)
- return;
-
- int ncompletions = completions.Length;
- if (ncompletions == 0)
- return;
-
- if (completions.Length == 1){
- InsertTextAtCursor (completions [0]);
- } else {
- int last = -1;
-
- for (int p = 0; p < completions [0].Length; p++){
- char c = completions [0][p];
-
-
- for (int i = 1; i < ncompletions; i++){
- if (completions [i].Length < p)
- goto mismatch;
-
- if (completions [i][p] != c){
- goto mismatch;
- }
- }
- last = p;
- }
- mismatch:
- if (last != -1){
- InsertTextAtCursor (completions [0].Substring (0, last+1));
- }
- Console.WriteLine ();
- foreach (string s in completions){
- Console.Write (completion.Prefix);
- Console.Write (s);
- Console.Write (' ');
- }
- Console.WriteLine ();
- Render ();
- ForceCursor (cursor);
- }
- } else
- HandleChar ('\t');
- } else
- HandleChar ('t');
- }
-
- void CmdHome ()
- {
- UpdateCursor (0);
- }
-
- void CmdEnd ()
- {
- UpdateCursor (text.Length);
- }
-
- void CmdLeft ()
- {
- if (cursor == 0)
- return;
-
- UpdateCursor (cursor-1);
- }
-
- void CmdBackwardWord ()
- {
- int p = WordBackward (cursor);
- if (p == -1)
- return;
- UpdateCursor (p);
- }
-
- void CmdForwardWord ()
- {
- int p = WordForward (cursor);
- if (p == -1)
- return;
- UpdateCursor (p);
- }
-
- void CmdRight ()
- {
- if (cursor == text.Length)
- return;
-
- UpdateCursor (cursor+1);
- }
-
- void RenderAfter (int p)
- {
- ForceCursor (p);
- RenderFrom (p);
- ForceCursor (cursor);
- }
-
- void CmdBackspace ()
- {
- if (cursor == 0)
- return;
-
- text.Remove (--cursor, 1);
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- void CmdDeleteChar ()
- {
- // If there is no input, this behaves like EOF
- if (text.Length == 0){
- done = true;
- text = null;
- Console.WriteLine ();
- return;
- }
-
- if (cursor == text.Length)
- return;
- text.Remove (cursor, 1);
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- int WordForward (int p)
- {
- if (p >= text.Length)
- return -1;
-
- int i = p;
- if (Char.IsPunctuation (text [p]) || Char.IsSymbol (text [p]) || Char.IsWhiteSpace (text[p])){
- for (; i < text.Length; i++){
- if (Char.IsLetterOrDigit (text [i]))
- break;
- }
- for (; i < text.Length; i++){
- if (!Char.IsLetterOrDigit (text [i]))
- break;
- }
- } else {
- for (; i < text.Length; i++){
- if (!Char.IsLetterOrDigit (text [i]))
- break;
- }
- }
- if (i != p)
- return i;
- return -1;
- }
-
- int WordBackward (int p)
- {
- if (p == 0)
- return -1;
-
- int i = p-1;
- if (i == 0)
- return 0;
-
- if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){
- for (; i >= 0; i--){
- if (Char.IsLetterOrDigit (text [i]))
- break;
- }
- for (; i >= 0; i--){
- if (!Char.IsLetterOrDigit (text[i]))
- break;
- }
- } else {
- for (; i >= 0; i--){
- if (!Char.IsLetterOrDigit (text [i]))
- break;
- }
- }
- i++;
-
- if (i != p)
- return i;
-
- return -1;
- }
-
- void CmdDeleteWord ()
- {
- int pos = WordForward (cursor);
-
- if (pos == -1)
- return;
-
- string k = text.ToString (cursor, pos-cursor);
-
- if (last_handler == CmdDeleteWord)
- kill_buffer = kill_buffer + k;
- else
- kill_buffer = k;
-
- text.Remove (cursor, pos-cursor);
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- void CmdDeleteBackword ()
- {
- int pos = WordBackward (cursor);
- if (pos == -1)
- return;
-
- string k = text.ToString (pos, cursor-pos);
-
- if (last_handler == CmdDeleteBackword)
- kill_buffer = k + kill_buffer;
- else
- kill_buffer = k;
-
- text.Remove (pos, cursor-pos);
- ComputeRendered ();
- RenderAfter (pos);
- }
-
- //
- // Adds the current line to the history if needed
- //
- void HistoryUpdateLine ()
- {
- history.Update (text.ToString ());
- }
-
- void CmdHistoryPrev ()
- {
- if (!history.PreviousAvailable ())
- return;
-
- HistoryUpdateLine ();
-
- SetText (history.Previous ());
- }
-
- void CmdHistoryNext ()
- {
- if (!history.NextAvailable())
- return;
-
- history.Update (text.ToString ());
- SetText (history.Next ());
-
- }
-
- void CmdKillToEOF ()
- {
- kill_buffer = text.ToString (cursor, text.Length-cursor);
- text.Length = cursor;
- ComputeRendered ();
- RenderAfter (cursor);
- }
-
- void CmdYank ()
- {
- InsertTextAtCursor (kill_buffer);
- }
-
- void InsertTextAtCursor (string str)
- {
- int prev_lines = LineCount;
- text.Insert (cursor, str);
- ComputeRendered ();
- if (prev_lines != LineCount){
- Console.SetCursorPosition (0, home_row);
- Render ();
- cursor += str.Length;
- ForceCursor (cursor);
- } else {
- RenderFrom (cursor);
- cursor += str.Length;
- ForceCursor (cursor);
- UpdateHomeRow (TextToScreenPos (cursor));
- }
- }
-
- void SetSearchPrompt (string s)
- {
- SetPrompt ("(reverse-i-search)`" + s + "': ");
- }
-
- void ReverseSearch ()
- {
- int p;
-
- if (cursor == text.Length){
- // The cursor is at the end of the string
-
- p = text.ToString ().LastIndexOf (search);
- if (p != -1){
- match_at = p;
- cursor = p;
- ForceCursor (cursor);
- return;
- }
- } else {
- // The cursor is somewhere in the middle of the string
- int start = (cursor == match_at) ? cursor - 1 : cursor;
- if (start != -1){
- p = text.ToString ().LastIndexOf (search, start);
- if (p != -1){
- match_at = p;
- cursor = p;
- ForceCursor (cursor);
- return;
- }
- }
- }
-
- // Need to search backwards in history
- HistoryUpdateLine ();
- string s = history.SearchBackward (search);
- if (s != null){
- match_at = -1;
- SetText (s);
- ReverseSearch ();
- }
- }
-
- void CmdReverseSearch ()
- {
- if (searching == 0){
- match_at = -1;
- last_search = search;
- searching = -1;
- search = "";
- SetSearchPrompt ("");
- } else {
- if (search == ""){
- if (last_search != "" && last_search != null){
- search = last_search;
- SetSearchPrompt (search);
-
- ReverseSearch ();
- }
- return;
- }
- ReverseSearch ();
- }
- }
-
- void SearchAppend (char c)
- {
- search = search + c;
- SetSearchPrompt (search);
-
- //
- // If the new typed data still matches the current text, stay here
- //
- if (cursor < text.Length){
- string r = text.ToString (cursor, text.Length - cursor);
- if (r.StartsWith (search))
- return;
- }
-
- ReverseSearch ();
- }
-
- void CmdRefresh ()
- {
- Console.Clear ();
- max_rendered = 0;
- Render ();
- ForceCursor (cursor);
- }
-
- void InterruptEdit (object sender, ConsoleCancelEventArgs a)
- {
- // Do not abort our program:
- a.Cancel = true;
-
- // Interrupt the editor
- edit_thread.Abort();
- }
-
- void HandleChar (char c)
- {
- if (searching != 0)
- SearchAppend (c);
- else
- InsertChar (c);
- }
-
- void EditLoop ()
- {
- ConsoleKeyInfo cki;
-
- while (!done){
- ConsoleModifiers mod;
-
- cki = Console.ReadKey (true);
- if (cki.Key == ConsoleKey.Escape){
- cki = Console.ReadKey (true);
-
- mod = ConsoleModifiers.Alt;
- } else
- mod = cki.Modifiers;
-
- bool handled = false;
-
- foreach (Handler handler in handlers){
- ConsoleKeyInfo t = handler.CKI;
-
- if (t.Key == cki.Key && t.Modifiers == mod){
- handled = true;
- handler.KeyHandler ();
- last_handler = handler.KeyHandler;
- break;
- } else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){
- handled = true;
- handler.KeyHandler ();
- last_handler = handler.KeyHandler;
- break;
- }
- }
- if (handled){
- if (searching != 0){
- if (last_handler != CmdReverseSearch){
- searching = 0;
- SetPrompt (prompt);
- }
- }
- continue;
- }
-
- if (cki.KeyChar != (char) 0)
- HandleChar (cki.KeyChar);
- }
- }
-
- void InitText (string initial)
- {
- text = new StringBuilder (initial);
- ComputeRendered ();
- cursor = text.Length;
- Render ();
- ForceCursor (cursor);
- }
-
- void SetText (string newtext)
- {
- Console.SetCursorPosition (0, home_row);
- InitText (newtext);
- }
-
- void SetPrompt (string newprompt)
- {
- shown_prompt = newprompt;
- Console.SetCursorPosition (0, home_row);
- Render ();
- ForceCursor (cursor);
- }
-
- public string Edit (string prompt, string initial)
- {
- edit_thread = Thread.CurrentThread;
- searching = 0;
- Console.CancelKeyPress += InterruptEdit;
-
- done = false;
- history.CursorToEnd ();
- max_rendered = 0;
-
- Prompt = prompt;
- shown_prompt = prompt;
- InitText (initial);
- history.Append (initial);
-
- do {
- try {
- EditLoop ();
- } catch (ThreadAbortException){
- searching = 0;
- Thread.ResetAbort ();
- Console.WriteLine ();
- SetPrompt (prompt);
- SetText ("");
- }
- } while (!done);
- Console.WriteLine ();
-
- Console.CancelKeyPress -= InterruptEdit;
-
- if (text == null){
- history.Close ();
- return null;
- }
-
- string result = text.ToString ();
- if (result != "")
- history.Accept (result);
- else
- history.RemoveLast ();
-
- return result;
- }
-
- public void SaveHistory ()
- {
- if (history != null) {
- history.Close ();
- }
- }
-
- public bool TabAtStartCompletes { get; set; }
-
- //
- // Emulates the bash-like behavior, where edits done to the
- // history are recorded
- //
- class History {
- string [] history;
- int head, tail;
- int cursor, count;
- string histfile;
-
- public History (string app, int size)
- {
- if (size < 1)
- throw new ArgumentException ("size");
-
- if (app != null){
- string dir = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
- //Console.WriteLine (dir);
- /*
- if (!Directory.Exists (dir)){
- try {
- Directory.CreateDirectory (dir);
- } catch {
- app = null;
- }
- }
- if (app != null)
- histfile = Path.Combine (dir, app) + ".history";
- */
- histfile = Path.Combine (dir, ".mal-history");
- }
-
- history = new string [size];
- head = tail = cursor = 0;
-
- if (File.Exists (histfile)){
- using (StreamReader sr = File.OpenText (histfile)){
- string line;
-
- while ((line = sr.ReadLine ()) != null){
- if (line != "")
- Append (line);
- }
- }
- }
- }
-
- public void Close ()
- {
- if (histfile == null)
- return;
-
- try {
- using (StreamWriter sw = File.CreateText (histfile)){
- int start = (count == history.Length) ? head : tail;
- for (int i = start; i < start+count; i++){
- int p = i % history.Length;
- sw.WriteLine (history [p]);
- }
- }
- } catch {
- // ignore
- }
- }
-
- //
- // Appends a value to the history
- //
- public void Append (string s)
- {
- //Console.WriteLine ("APPENDING {0} head={1} tail={2}", s, head, tail);
- history [head] = s;
- head = (head+1) % history.Length;
- if (head == tail)
- tail = (tail+1 % history.Length);
- if (count != history.Length)
- count++;
- //Console.WriteLine ("DONE: head={1} tail={2}", s, head, tail);
- }
-
- //
- // Updates the current cursor location with the string,
- // to support editing of history items. For the current
- // line to participate, an Append must be done before.
- //
- public void Update (string s)
- {
- history [cursor] = s;
- }
-
- public void RemoveLast ()
- {
- head = head-1;
- if (head < 0)
- head = history.Length-1;
- }
-
- public void Accept (string s)
- {
- int t = head-1;
- if (t < 0)
- t = history.Length-1;
-
- history [t] = s;
- }
-
- public bool PreviousAvailable ()
- {
- //Console.WriteLine ("h={0} t={1} cursor={2}", head, tail, cursor);
- if (count == 0)
- return false;
- int next = cursor-1;
- if (next < 0)
- next = count-1;
-
- if (next == head)
- return false;
-
- return true;
- }
-
- public bool NextAvailable ()
- {
- if (count == 0)
- return false;
- int next = (cursor + 1) % history.Length;
- if (next == head)
- return false;
- return true;
- }
-
-
- //
- // Returns: a string with the previous line contents, or
- // nul if there is no data in the history to move to.
- //
- public string Previous ()
- {
- if (!PreviousAvailable ())
- return null;
-
- cursor--;
- if (cursor < 0)
- cursor = history.Length - 1;
-
- return history [cursor];
- }
-
- public string Next ()
- {
- if (!NextAvailable ())
- return null;
-
- cursor = (cursor + 1) % history.Length;
- return history [cursor];
- }
-
- public void CursorToEnd ()
- {
- if (head == tail)
- return;
-
- cursor = head;
- }
-
- public void Dump ()
- {
- Console.WriteLine ("Head={0} Tail={1} Cursor={2} count={3}", head, tail, cursor, count);
- for (int i = 0; i < history.Length;i++){
- Console.WriteLine (" {0} {1}: {2}", i == cursor ? "==>" : " ", i, history[i]);
- }
- //log.Flush ();
- }
-
- public string SearchBackward (string term)
- {
- for (int i = 0; i < count; i++){
- int slot = cursor-i-1;
- if (slot < 0)
- slot = history.Length+slot;
- if (slot >= history.Length)
- slot = 0;
- if (history [slot] != null && history [slot].IndexOf (term) != -1){
- cursor = slot;
- return history [slot];
- }
- }
-
- return null;
- }
-
- }
- }
-
-#if DEMO
- class Demo {
- static void Main ()
- {
- LineEditor le = new LineEditor ("foo");
- string s;
-
- while ((s = le.Edit ("shell> ", "")) != null){
- Console.WriteLine ("----> [{0}]", s);
- }
- }
- }
-#endif
-}