зеркало из
1
0
Форкнуть 0
linux-packaging-mono-tools/gsharp/Shell.cs

630 строки
16 KiB
C#

/***************************************************************************
* CSharpShell.cs, based on the BooShell.cs from Banshee.
*
* Copyright (C) 2006-2008 Novell, Inc.
* Written by Aaron Bockover <aaron@abock.org>.
* Miguel de Icaza (miguel@gnome.org).
*
* Based on ShellTextView.boo in the MonoDevelop Boo Binding
* originally authored by Peter Johanson <latexer@gentoo.org>
****************************************************************************/
/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW:
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using Gtk;
using Mono.CSharp;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using System.Linq;
namespace Mono.CSharp.Gui
{
[ToolboxItem (true)]
public class Shell : TextView
{
TextMark end_of_last_processing;
string expr;
Evaluator evaluator;
CompilerContext context;
List<string> history = new List<string> ();
int history_cursor;
MainWindow container;
public MainWindow Container { get { return container; }}
public Shell(MainWindow container) : base()
{
this.container = container;
WrapMode = WrapMode.Word;
CreateTags ();
Pango.FontDescription font_description = new Pango.FontDescription();
font_description.Family = "Monospace";
ModifyFont(font_description);
TextIter end = Buffer.EndIter;
Buffer.InsertWithTagsByName (ref end, "Mono C# Shell, type 'help;' for help\n\nEnter statements or expressions below.\n", "Comment");
ShowPrompt (false);
context = new CompilerContext (new CompilerSettings (), new ConsoleReportPrinter ());
evaluator = new Evaluator (context);
evaluator.DescribeTypeExpressions = true;
evaluator.InteractiveBaseClass = typeof (InteractiveGraphicsBase);
evaluator.Run ("LoadAssembly (\"System.Drawing\");");
evaluator.Run ("using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Drawing;");
if (!MainClass.Debug){
GuiStream error_stream = new GuiStream ("Error", (x, y) => Output (x, y));
StreamWriter gui_output = new StreamWriter (error_stream);
gui_output.AutoFlush = true;
Console.SetError (gui_output);
GuiStream stdout_stream = new GuiStream ("Stdout", (x, y) => Output (x, y));
gui_output = new StreamWriter (stdout_stream);
gui_output.AutoFlush = true;
Console.SetOut (gui_output);
}
}
void CreateTags ()
{
TextTag freeze_tag = new TextTag("Freezer") {
Editable = false
};
Buffer.TagTable.Add(freeze_tag);
TextTag prompt_tag = new TextTag("Prompt") {
Foreground = "blue",
//Background = "#f8f8f8",
Weight = Pango.Weight.Bold
};
Buffer.TagTable.Add(prompt_tag);
TextTag prompt_continuation_tag = new TextTag("PromptContinuation") {
Foreground = "orange",
//Background = "#f8f8f8",
Weight = Pango.Weight.Bold
};
Buffer.TagTable.Add(prompt_continuation_tag);
TextTag error_tag = new TextTag("Error") {
Foreground = "red"
};
Buffer.TagTable.Add(error_tag);
TextTag stdout_tag = new TextTag("Stdout") {
Foreground = "#006600"
};
Buffer.TagTable.Add(stdout_tag);
TextTag comment = new TextTag ("Comment") {
Foreground = "#3f7f5f"
};
Buffer.TagTable.Add (comment);
}
public event EventHandler QuitRequested;
//
// Returns true if the line is complete, so that the line can be entered
// into the history, false if this was partial
//
public bool Evaluate (string s)
{
string res = null;
object result;
bool result_set;
StringWriter errorwriter = new StringWriter ();
var old_printer = context.Report.SetPrinter (new StreamReportPrinter (errorwriter));
try {
res = evaluator.Evaluate (s, out result, out result_set);
} catch (Exception e){
expr = null;
ShowError (e.ToString ());
ShowPrompt (true, false);
return true;
} finally {
context.Report.SetPrinter (old_printer);
}
// Partial input
if (res != null){
ShowPrompt (false, true);
return false;
}
string error = errorwriter.ToString ();
if (error.Length > 0){
ShowError (error);
ShowPrompt (false, false);
} else {
if (result_set){
ShowResult (result);
ShowPrompt (true, false);
} else
ShowPrompt (false, false);
}
expr = null;
return true;
}
string GetCurrentExpression ()
{
string input = InputLine;
return expr == null ? input : expr + "\n" + input;
}
[Conditional("DEBUG_HISTORY")]
void DumpHistory ()
{
for (int i = 0; i < history.Count; i++)
Console.WriteLine ("{0} {1}: {2}",
i == history_cursor ? "==>" : " ",
i, history [i]);
Console.WriteLine ("--- {0} --- ", history_cursor);
}
protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
{
if(Cursor.Compare (InputLineBegin) < 0) {
Buffer.MoveMark(Buffer.SelectionBound, InputLineEnd);
Buffer.MoveMark(Buffer.InsertMark, InputLineEnd);
}
switch (evnt.Key){
case Gdk.Key.Return:
case Gdk.Key.KP_Enter:
string input_line = InputLine;
history [history.Count-1] = input_line;
history_cursor = history.Count;
string expr_copy = expr = GetCurrentExpression ();
// Insert a new line before we evaluate.
TextIter end = Buffer.EndIter;
Buffer.InsertWithTagsByName (ref end, "\n", "Stdout");
if (Evaluate (expr)){
if (expr_copy != input_line){
history.Add (expr_copy);
history_cursor = history.Count;
}
}
history.Add ("");
DumpHistory ();
if (InteractiveBase.QuitRequested && QuitRequested != null)
QuitRequested (this, EventArgs.Empty);
return true;
case Gdk.Key.Up:
if (history_cursor == 0){
DumpHistory ();
return true;
}
string input = InputLine;
if (!String.IsNullOrEmpty (input)){
DumpHistory ();
history [history_cursor] = input;
}
history_cursor--;
InputLine = (string) history [history_cursor];
DumpHistory ();
return true;
case Gdk.Key.Down:
if (history_cursor+1 >= history.Count){
DumpHistory ();
return true;
}
history_cursor++;
InputLine = (string) history [history_cursor];
DumpHistory ();
return true;
case Gdk.Key.Left:
if(Cursor.Compare(InputLineBegin) <= 0) {
return true;
}
break;
case Gdk.Key.Home:
Buffer.MoveMark(Buffer.InsertMark, InputLineBegin);
if((evnt.State & Gdk.ModifierType.ShiftMask) != Gdk.ModifierType.ShiftMask) {
Buffer.MoveMark(Buffer.SelectionBound, InputLineBegin);
}
return true;
case Gdk.Key.Tab:
string saved_text = InputLine;
string prefix;
string [] completions = evaluator.GetCompletions (LineUntilCursor, out prefix);
if (completions == null)
return true;
if (completions.Length == 1){
TextIter cursor = Cursor;
Buffer.Insert (ref cursor, completions [0]);
return true;
}
Console.WriteLine ();
foreach (var s in completions){
Console.Write (prefix);
Console.Write (s);
Console.Write (" ");
}
// Insert a new line before we evaluate.
end = Buffer.EndIter;
Buffer.InsertWithTagsByName (ref end, "\n", "Stdout");
ShowPrompt (false);
InputLine = saved_text;
#if false
Gtk.TextIter start = Cursor;
if (prefix.Length != 0)
MoveVisually (ref start, -prefix.Length);
int x, y;
GdkWindow.GetOrigin (out x, out y);
var r = GetIterLocation (start);
x += r.X;
y += r.Y;
var w = new Gtk.Window (WindowType.Popup);
w.SetUposition (x, y);
w.SetUsize (100, 100);
foreach (var s in completions){
Console.WriteLine ("{0}[{1}]", prefix, s);
}
w.ShowAll ();
Console.WriteLine ("Position: x={0} y={1}", x + r.X, y +r.Y);
#endif
return true;
default:
break;
}
return base.OnKeyPressEvent(evnt);
}
public void ShowPrompt(bool newline)
{
ShowPrompt (newline, false);
}
private void ShowPrompt (bool newline, bool continuation)
{
TextIter end_iter = Buffer.EndIter;
if(newline) {
Buffer.Insert(ref end_iter, "\n");
}
string prompt = continuation ? InteractiveBase.ContinuationPrompt : InteractiveBase.Prompt;
Buffer.Insert(ref end_iter, prompt);
Buffer.PlaceCursor(Buffer.EndIter);
ScrollMarkOnscreen(Buffer.InsertMark);
end_of_last_processing = Buffer.CreateMark(null, Buffer.EndIter, true);
Buffer.ApplyTag(Buffer.TagTable.Lookup("Freezer"), Buffer.StartIter, InputLineBegin);
TextIter prompt_start_iter = InputLineBegin;
prompt_start_iter.LineIndex -= prompt.Length;
TextIter prompt_end_iter = InputLineBegin;
prompt_end_iter.LineIndex -= 1;
Buffer.ApplyTag(Buffer.TagTable.Lookup(continuation ? "PromptContinuation" : "Prompt"),
prompt_start_iter, prompt_end_iter);
}
public void Output (string kind, string s)
{
TextIter end = Buffer.EndIter;
Buffer.InsertWithTagsByName (ref end, s, kind);
}
public void ShowResult (object res)
{
TextIter end = Buffer.EndIter;
var handlers = new List<InteractiveGraphicsBase.TransformHandler> (InteractiveGraphicsBase.type_handlers);
//object original = res;
bool retry;
do {
retry = false;
foreach (var render_handler in handlers){
object transformed = render_handler (res);
if (transformed == null || transformed == res)
continue;
if (transformed is Gtk.Widget){
Gtk.Widget w = (Gtk.Widget) transformed;
TextChildAnchor anchor = Buffer.CreateChildAnchor (ref end);
w.Show ();
AddChildAtAnchor (w, anchor);
return;
} else {
res = transformed;
handlers.Remove (render_handler);
retry = true;
break;
}
}
} while (retry && handlers.Count > 0);
StringWriter pretty = new StringWriter ();
try {
PrettyPrint (pretty, res);
} catch (Exception e) {
Console.WriteLine (e);
}
Buffer.InsertWithTagsByName (ref end, pretty.ToString (), "Stdout");
}
public void ShowError (string err)
{
TextIter end = Buffer.EndIter;
Buffer.InsertWithTagsByName (ref end, err, "Error");
}
TextIter InputLineBegin {
get { return Buffer.GetIterAtMark(end_of_last_processing); }
}
TextIter InputLineEnd {
get { return Buffer.EndIter; }
}
TextIter Cursor {
get { return Buffer.GetIterAtMark(Buffer.InsertMark); }
}
public Evaluator Evaluator {
get {
return evaluator;
}
}
string InputLine {
get { return Buffer.GetText(InputLineBegin, InputLineEnd, false); }
set {
TextIter start = InputLineBegin;
TextIter end = InputLineEnd;
Buffer.Delete(ref start, ref end);
start = InputLineBegin;
Buffer.Insert(ref start, value);
ScrollMarkOnscreen(Buffer.InsertMark);
}
}
string LineUntilCursor {
get {
return Buffer.GetText (InputLineBegin, Cursor, false);
}
}
static void p (TextWriter output, string s)
{
output.Write (s);
}
static string EscapeString (string s)
{
return s.Replace ("\"", "\\\"");
}
static void EscapeChar (TextWriter output, char c)
{
if (c == '\''){
output.Write ("'\\''");
return;
}
if (c > 32){
output.Write ("'{0}'", c);
return;
}
switch (c){
case '\a':
output.Write ("'\\a'");
break;
case '\b':
output.Write ("'\\b'");
break;
case '\n':
output.Write ("'\\n'");
break;
case '\v':
output.Write ("'\\v'");
break;
case '\r':
output.Write ("'\\r'");
break;
case '\f':
output.Write ("'\\f'");
break;
case '\t':
output.Write ("'\\t");
break;
default:
output.Write ("'\\x{0:x}", (int) c);
break;
}
}
internal XElement SaveHistory ()
{
var doc = new XElement (
"history",
from x in history
select new XElement ("item", x));
return doc;
}
internal void LoadHistory (IEnumerable<XElement> items)
{
if (items == null){
history.Add ("");
return;
}
foreach (var e in items){
string s = e.Value;
if (s == null || s.Length == 0)
continue;
history.Add (s);
}
history.Add ("");
history_cursor = history.Count-1;
}
internal static void PrettyPrint (TextWriter output, object result)
{
if (result == null){
p (output, "null");
return;
}
if (result is Array){
Array a = (Array) result;
p (output, "{ ");
int top = a.GetUpperBound (0);
for (int i = a.GetLowerBound (0); i <= top; i++){
PrettyPrint (output, a.GetValue (i));
if (i != top)
p (output, ", ");
}
p (output, " }");
} else if (result is bool){
if ((bool) result)
p (output, "true");
else
p (output, "false");
} else if (result is string){
p (output, String.Format ("\"{0}\"", EscapeString ((string)result)));
} else if (result is IDictionary){
IDictionary dict = (IDictionary) result;
int top = dict.Count, count = 0;
p (output, "{");
foreach (DictionaryEntry entry in dict){
count++;
p (output, "{ ");
try {
PrettyPrint (output, entry.Key);
} catch {
p (output, "<error>");
}
p (output, ", ");
try {
PrettyPrint (output, entry.Value);
} catch {
p (output, "<error>");
}
if (count != top)
p (output, " }, ");
else
p (output, " }");
}
p (output, "}");
} else if (result is IEnumerable) {
int i = 0;
p (output, "{ ");
try {
foreach (object item in (IEnumerable) result) {
if (i++ != 0)
p (output, ", ");
PrettyPrint (output, item);
}
} catch {
p (output, "<error>");
}
p (output, " }");
} else if (result is char){
EscapeChar (output, (char) result);
} else {
try {
p (output, result.ToString ());
} catch {
p (output, "<error>");
}
}
}
}
public class GuiStream : Stream {
string kind;
Action<string,string> callback;
public GuiStream (string k, Action<string, string> cb)
{
kind = k;
callback = cb;
}
public override bool CanRead { get { return false; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return true; } }
public override long Length { get { return 0; } }
public override long Position { get { return 0; } set {} }
public override void Flush () { }
public override int Read ([In,Out] byte[] buffer, int offset, int count) { return -1; }
public override long Seek (long offset, SeekOrigin origin) { return 0; }
public override void SetLength (long value) { }
public override void Write (byte[] buffer, int offset, int count) {
callback (kind, Encoding.UTF8.GetString (buffer, offset, count));
}
}
}