[Mono-list] ECall methods must be packaged into a system module.
Antonello Provenzano
antonello at minosse.com
Sun Apr 16 15:20:52 EDT 2006
I created an application that emulates the GNU ReadLine library,
entirely written in C# language, merging part of the sources of the
Mono Console API and the project DotGNU.Terminal (you can checkout it
at http://sourceforge.net/projects/deveelreadline).
When I started testing it, I received an exception, wrapped by a
TypeInitializationException, with the message "ECall methods must be
packaged into a system module.", both under Mono and .NET.
The only internal calls in this library are made inside the
"redefined" Mono Console API included in the library, but when I
tested it, worked perfectly without any problem.
The error, then, is generated by the ReadLine class (a static class
without any constructor: this makes me mad about the fact the
exception thrown is a TypeInitializationException, typically
originated by a constructor of a type).
I would like to receive help from you about this topic: you will find
the ReadLine class attached, even if the complete project can be
downloaded from the project page on SourceForge (the one described
above).
Thanks.
Antonello
-------------- next part --------------
using System;
namespace Deveel {
public sealed class Readline {
#region ctor
private Readline() {
}
#endregion
#region Fields
// Internal state.
private static bool enterIsDuplicate = false;
private static bool controlDIsEOF = true;
private static bool controlZIsEOF = Console.IsWindows();
// Line input buffer.
private static char[] buffer = new char[256];
private static byte[] widths = new byte[256];
private static int posn, length, column, lastColumn;
private static bool overwrite = false;
private static int historyPosn;
private static string historySave;
private static string yankedString = null;
#endregion
#region Events
/// <summary>
/// Event that is emitted to allow for tab completion.
/// </summary>
/// <remarks>
/// If there are no attached handlers, then the Tab key will do
/// normal tabbing.
/// </remarks>
public static event TabCompleteEventHandler TabComplete;
#endregion
#region Properties
/// <summary>
/// Gets or sets a flag that indicates if pressing the "Enter" key on
/// an empty line causes the most recent history line to be duplicated.
/// </summary>
public static bool EnterIsDuplicate {
get { return enterIsDuplicate; }
set { enterIsDuplicate = value; }
}
/// <summary>
/// Gets or sets a flag that indicates if CTRL-D is an EOF indication
/// or the "delete character" key.
/// </summary>
/// <remarks>
/// The default is true (i.e. EOF).
/// </remarks>
public static bool ControlDIsEOF {
get { return controlDIsEOF; }
set { controlDIsEOF = value; }
}
/// <summary>
/// Gets or sets a flag that indicates if CTRL-Z is an EOF indication.
/// </summary>
/// <remarks>
/// The default is true on Windows system, false otherwise.
/// </remarks>
public static bool ControlZIsEOF {
get { return controlZIsEOF; }
set { controlZIsEOF = value; }
}
#endregion
#region Private Static Methods
/// <summary>
/// Makes room for one more character in the input buffer.
/// </summary>
private static void MakeRoom() {
if (length >= buffer.Length) {
char[] newBuffer = new char[buffer.Length * 2];
byte[] newWidths = new byte[buffer.Length * 2];
Array.Copy(buffer, 0, newBuffer, 0, buffer.Length);
Array.Copy(widths, 0, newWidths, 0, buffer.Length);
}
}
/// <summary>
/// Repaint the line starting at the current character.
/// </summary>
/// <param name="step"></param>
/// <param name="moveToEnd"></param>
private static void Repaint(bool step, bool moveToEnd) {
int posn = Readline.posn;
int column = Readline.column;
int width;
// Paint the characters in the line.
while (posn < length) {
if (buffer[posn] == '\t') {
width = 8 - (column % 8);
widths[posn] = (byte)width;
while (width > 0) {
Console.Write(' ');
--width;
++column;
}
} else if (buffer[posn] < 0x20) {
Console.Write('^');
Console.Write((char)(buffer[posn] + 0x40));
widths[posn] = 2;
column += 2;
} else if (buffer[posn] == '\u007F') {
Console.Write('^');
Console.Write('?');
widths[posn] = 2;
column += 2;
} else {
Console.Write(buffer[posn]);
widths[posn] = 1;
++column;
}
++posn;
}
// Adjust the position of the last column.
if (column > lastColumn) {
lastColumn = column;
} else if (column < lastColumn) {
// We need to clear some characters beyond this point.
width = lastColumn - column;
lastColumn = column;
while (width > 0) {
Console.Write(' ');
--width;
++column;
}
}
// Backspace to the initial cursor position.
if (moveToEnd) {
width = column - lastColumn;
Readline.posn = length;
} else if (step) {
width = column - (Readline.column + widths[Readline.posn]);
Readline.column += widths[Readline.posn];
++(Readline.posn);
} else {
width = column - Readline.column;
}
while (width > 0) {
Console.Write('\u0008');
--width;
}
}
/// <summary>
/// Add a character to the input buffer.
/// </summary>
/// <param name="ch"></param>
private static void AddChar(char ch) {
if (overwrite && posn < length) {
buffer[posn] = ch;
Repaint(true, false);
} else {
MakeRoom();
if (posn < length) {
Array.Copy(buffer, posn, buffer, posn + 1,
length - posn);
}
buffer[posn] = ch;
++length;
Repaint(true, false);
}
}
// Go back a specific number of characters.
private static void GoBack(int num) {
int width;
while (num > 0) {
--posn;
width = widths[posn];
column -= width;
while (width > 0) {
Console.Write('\u0008');
--width;
}
--num;
}
}
// Backspace one character.
private static void Backspace() {
if (posn > 0) {
GoBack(1);
Delete();
}
}
// Delete the character under the cursor.
private static void Delete() {
if (posn < length) {
Array.Copy(buffer, posn + 1, buffer, posn,
length - posn - 1);
--length;
Repaint(false, false);
}
}
// Delete a number of characters under the cursor.
private static void Delete(int num) {
Array.Copy(buffer, posn + num, buffer, posn,
length - posn - num);
length -= num;
Repaint(false, false);
}
// Print a list of alternatives for tab completion.
private static void PrintAlternatives(String[] list) {
int width, maxWidth;
int columns, column, posn;
String str;
// Determine the maximum string length, for formatting.
maxWidth = 0;
foreach (String a in list) {
if (a != null) {
width = a.Length;
if (width > maxWidth) {
maxWidth = width;
}
}
}
// Determine the number of columns.
width = Console.WindowWidth;
if (maxWidth > (width - 7)) {
columns = 1;
} else {
columns = width / (maxWidth + 7);
}
// Print the strings.
column = 0;
for (posn = 0; posn < list.Length; ++posn) {
str = list[posn];
if (str != null) {
Console.Write(str);
width = str.Length;
} else {
width = 0;
}
++column;
if (column < columns) {
while (width < maxWidth) {
Console.Write(' ');
++width;
}
Console.Write(" ");
} else {
Console.Write("\r\n");
column = 0;
}
}
if (column != 0) {
Console.Write("\r\n");
}
}
// Tab across to the next stop, or perform tab completion.
private static void Tab(String prompt) {
if (TabComplete == null) {
// Add the TAB character and repaint the line.
AddChar('\t');
} else {
// Perform tab completion and insert the results.
TabCompleteEventArgs e;
e = new TabCompleteEventArgs(new String(buffer, 0, posn), new String(buffer, posn, length - posn));
TabComplete(null, e);
if (e.Insert != null) {
// Insert the value that we found.
bool saveOverwrite = overwrite;
overwrite = false;
foreach (char ch in e.Insert) {
AddChar(ch);
}
overwrite = saveOverwrite;
} else if (e.Alternatives != null && e.Alternatives.Length > 0) {
// Print the alternatives for the user.
int savePosn = posn;
EndLine();
PrintAlternatives(e.Alternatives);
if (prompt != null) {
Console.Write(prompt);
}
posn = savePosn;
Redraw();
} else {
// No alternatives, or alternatives not supplied yet.
Console.Beep();
}
}
}
// End the current line.
private static void EndLine() {
// Repaint the line and move to the end.
Repaint(false, true);
// Output the line terminator to the terminal.
Console.WriteLine();
}
// Move left one character.
private static void MoveLeft() {
if (posn > 0) {
GoBack(1);
}
}
// Move right one character.
private static void MoveRight() {
if (posn < length) {
Repaint(true, false);
}
}
// Set the current buffer contents to a historical string.
private static void SetCurrent(String line) {
if (line == null) {
line = String.Empty;
}
Clear();
foreach (char ch in line) {
AddChar(ch);
}
}
// Move up one line in the history.
private static void MoveUp() {
if (historyPosn == -1) {
if (History.Count > 0) {
historySave = new String(buffer, 0, length);
historyPosn = 0;
SetCurrent(History.GetHistory(historyPosn));
}
} else if ((historyPosn + 1) < History.Count) {
++historyPosn;
SetCurrent(History.GetHistory(historyPosn));
} else {
Console.Beep();
}
}
// Move down one line in the history.
private static void MoveDown() {
if (historyPosn == 0) {
historyPosn = -1;
SetCurrent(historySave);
} else if (historyPosn > 0) {
--historyPosn;
SetCurrent(History.GetHistory(historyPosn));
} else {
Console.Beep();
}
}
// Move to the beginning of the current line.
private static void MoveHome() {
GoBack(posn);
}
// Move to the end of the current line.
private static void MoveEnd() {
Repaint(false, true);
}
// Clear the entire line.
private static void Clear() {
GoBack(posn);
length = 0;
Repaint(false, false);
}
// Cancel the current line and start afresh with a new prompt.
private static void CancelLine(String prompt) {
EndLine();
if (prompt != null) {
Console.Write(prompt);
}
posn = 0;
length = 0;
column = 0;
lastColumn = 0;
historyPosn = -1;
}
// Redraw the current line.
private static void Redraw() {
String str = new String(buffer, 0, length);
int savePosn = posn;
posn = 0;
length = 0;
column = 0;
lastColumn = 0;
foreach (char ch in str) {
AddChar(ch);
}
GoBack(length - savePosn);
}
// Erase all characters until the start of the current line.
private static void EraseToStart() {
if (posn > 0) {
int savePosn = posn;
yankedString = new String(buffer, 0, posn);
GoBack(savePosn);
Delete(savePosn);
}
}
// Erase all characters until the end of the current line.
private static void EraseToEnd() {
yankedString = new String(buffer, posn, length - posn);
length = posn;
Repaint(false, false);
}
// Erase the previous word on the current line (delimited by whitespace).
private static void EraseWord() {
int temp = posn;
while (temp > 0 && Char.IsWhiteSpace(buffer[temp - 1])) {
--temp;
}
while (temp > 0 && !Char.IsWhiteSpace(buffer[temp - 1])) {
--temp;
}
if (temp < posn) {
temp = posn - temp;
GoBack(temp);
yankedString = new String(buffer, posn, temp);
Delete(temp);
}
}
// Determine if a character is a "word character" (letter or digit).
private static bool IsWordCharacter(char ch) {
return Char.IsLetterOrDigit(ch);
}
// Erase to the end of the current word.
private static void EraseToEndWord() {
int temp = posn;
while (temp < length && !IsWordCharacter(buffer[temp])) {
++temp;
}
while (temp < length && IsWordCharacter(buffer[temp])) {
++temp;
}
if (temp > posn) {
temp -= posn;
yankedString = new String(buffer, posn, temp);
Delete(temp);
}
}
// Erase to the start of the current word.
private static void EraseToStartWord() {
int temp = posn;
while (temp > 0 && !IsWordCharacter(buffer[temp - 1])) {
--temp;
}
while (temp > 0 && IsWordCharacter(buffer[temp - 1])) {
--temp;
}
if (temp < posn) {
temp = posn - temp;
GoBack(temp);
yankedString = new String(buffer, posn, temp);
Delete(temp);
}
}
// Move forward one word in the input line.
private static void MoveForwardWord() {
while (posn < length && !IsWordCharacter(buffer[posn])) {
MoveRight();
}
while (posn < length && IsWordCharacter(buffer[posn])) {
MoveRight();
}
}
// Move backward one word in the input line.
private static void MoveBackwardWord() {
while (posn > 0 && !IsWordCharacter(buffer[posn - 1])) {
MoveLeft();
}
while (posn > 0 && IsWordCharacter(buffer[posn - 1])) {
MoveLeft();
}
}
#endregion
#region Public Static Methods
// Read the next line of input using line editing. Returns "null"
// if an EOF indication is encountered in the input.
public static string ReadLine(string prompt) {
ConsoleKeyInfo key;
char ch;
bool done;
bool ctrlv;
// Output the prompt.
if (prompt != null) {
Console.Write(prompt);
}
// Enter the main character input loop.
posn = 0;
length = 0;
column = 0;
lastColumn = 0;
done = false;
overwrite = false;
historyPosn = -1;
ctrlv = false;
do {
key = ConsoleExtensions.ReadKey(true);
ch = key.KeyChar;
if (ctrlv) {
ctrlv = false;
if ((ch >= 0x0001 && ch <= 0x001F) || ch == 0x007F) {
// Insert a control character into the buffer.
AddChar(ch);
continue;
}
}
if (ch != '\0') {
switch (ch) {
case '\u0001': {
// CTRL-A: move to the home position.
MoveHome();
}
break;
case '\u0002': {
// CTRL-B: go back one character.
MoveLeft();
}
break;
case '\u0003': {
// CTRL-C encountered in "raw" mode.
CancelLine(prompt);
}
break;
case '\u0004': {
// CTRL-D: EOF or delete the current character.
if (controlDIsEOF) {
// Signal an EOF if the buffer is empty.
if (length == 0) {
EndLine();
return null;
}
} else {
Delete();
}
}
break;
case '\u0005': {
// CTRL-E: move to the end position.
MoveEnd();
}
break;
case '\u0006': {
// CTRL-F: go forward one character.
MoveRight();
}
break;
case '\u0007': {
// CTRL-G: ring the terminal bell.
Console.Beep();
}
break;
case '\u0008':
case '\u007F': {
if (key.Key == ConsoleKey.Delete) {
// Delete the character under the cursor.
Delete();
} else {
// Delete the character before the cursor.
Backspace();
}
}
break;
case '\u0009': {
// Process a tab.
Tab(prompt);
}
break;
case '\u000A':
case '\u000D': {
// Line termination.
EndLine();
done = true;
}
break;
case '\u000B': {
// CTRL-K: erase until the end of the line.
EraseToEnd();
}
break;
case '\u000C': {
// CTRL-L: clear screen and redraw.
Console.Clear();
Console.Write(prompt);
Redraw();
}
break;
case '\u000E': {
// CTRL-N: move down in the history.
MoveDown();
}
break;
case '\u0010': {
// CTRL-P: move up in the history.
MoveUp();
}
break;
case '\u0015': {
// CTRL-U: erase to the start of the line.
EraseToStart();
}
break;
case '\u0016': {
// CTRL-V: prefix a control character.
ctrlv = true;
}
break;
case '\u0017': {
// CTRL-W: erase the previous word.
EraseWord();
}
break;
case '\u0019': {
// CTRL-Y: yank the last erased string.
if (yankedString != null) {
foreach (char ch2 in yankedString) {
AddChar(ch2);
}
}
}
break;
case '\u001A': {
// CTRL-Z: Windows end of file indication.
if (controlZIsEOF && length == 0) {
EndLine();
return null;
}
}
break;
case '\u001B': {
// Escape is "clear line".
Clear();
}
break;
default: {
if (ch >= ' ') {
// Ordinary character.
AddChar(ch);
}
}
break;
}
} else if (key.Modifiers == (ConsoleModifiers)0) {
switch (key.Key) {
case ConsoleKey.Backspace: {
// Delete the character before the cursor.
Backspace();
}
break;
case ConsoleKey.Delete: {
// Delete the character under the cursor.
Delete();
}
break;
case ConsoleKey.Enter: {
// Line termination.
EndLine();
done = true;
}
break;
case ConsoleKey.Escape: {
// Clear the current line.
Clear();
}
break;
case ConsoleKey.Tab: {
// Process a tab.
Tab(prompt);
}
break;
case ConsoleKey.LeftArrow: {
// Move left one character.
MoveLeft();
}
break;
case ConsoleKey.RightArrow: {
// Move right one character.
MoveRight();
}
break;
case ConsoleKey.UpArrow: {
// Move up one line in the history.
MoveUp();
}
break;
case ConsoleKey.DownArrow: {
// Move down one line in the history.
MoveDown();
}
break;
case ConsoleKey.Home: {
// Move to the beginning of the line.
MoveHome();
}
break;
case ConsoleKey.End: {
// Move to the end of the line.
MoveEnd();
}
break;
case ConsoleKey.Insert: {
// Toggle insert/overwrite mode.
overwrite = !overwrite;
}
break;
}
} else if ((key.Modifiers & ConsoleModifiers.Alt) != 0) {
switch (key.Key) {
case ConsoleKey.F: {
// ALT-F: move forward a word.
MoveForwardWord();
}
break;
case ConsoleKey.B: {
// ALT-B: move backward a word.
MoveBackwardWord();
}
break;
case ConsoleKey.D: {
// ALT-D: erase until the end of the word.
EraseToEndWord();
}
break;
case ConsoleKey.Backspace:
case ConsoleKey.Delete: {
// ALT-DEL: erase until the start of the word.
EraseToStartWord();
}
break;
}
}
}
while (!done);
if (length == 0 && enterIsDuplicate) {
if (History.Count > 0) {
return History.GetHistory(0);
}
}
return new String(buffer, 0, length);
}
#endregion
}
}
More information about the Mono-list
mailing list