You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
10 KiB
324 lines
10 KiB
// <file> |
|
// <copyright see="prj:///doc/copyright.txt"/> |
|
// <license see="prj:///doc/license.txt"/> |
|
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/> |
|
// <version>$Revision$</version> |
|
// </file> |
|
|
|
using System; |
|
using System.CodeDom; |
|
using System.CodeDom.Compiler; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.ComponentModel; |
|
using System.ComponentModel.Design; |
|
using System.ComponentModel.Design.Serialization; |
|
using System.Drawing; |
|
using System.Reflection; |
|
using System.Text; |
|
using System.Windows.Forms; |
|
|
|
using ICSharpCode.FormsDesigner; |
|
using ICSharpCode.SharpDevelop; |
|
using ICSharpCode.SharpDevelop.Dom; |
|
using ICSharpCode.SharpDevelop.Editor; |
|
using ICSharpCode.SharpDevelop.Gui; |
|
using ICSharpCode.SharpDevelop.Project; |
|
|
|
namespace ICSharpCode.RubyBinding |
|
{ |
|
public interface IRubyDesignerGenerator : IDesignerGenerator |
|
{ |
|
/// <summary> |
|
/// Updates the Ruby form or user control's InitializeComponent method with any |
|
/// changes to the designed form or user control. |
|
/// </summary> |
|
void MergeRootComponentChanges(IDesignerHost host, IDesignerSerializationManager serializationManager); |
|
} |
|
|
|
/// <summary> |
|
/// Form's designer generator for the Ruby language. |
|
/// </summary> |
|
public class RubyDesignerGenerator : IRubyDesignerGenerator |
|
{ |
|
FormsDesignerViewContent viewContent; |
|
ITextEditorOptions textEditorOptions; |
|
|
|
public RubyDesignerGenerator(ITextEditorOptions textEditorOptions) |
|
{ |
|
this.textEditorOptions = textEditorOptions; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the Ruby code dom provider. |
|
/// </summary> |
|
public CodeDomProvider CodeDomProvider { |
|
get { return null; } |
|
} |
|
|
|
public void Attach(FormsDesignerViewContent viewContent) |
|
{ |
|
this.viewContent = viewContent; |
|
} |
|
|
|
public void Detach() |
|
{ |
|
this.viewContent = null; |
|
} |
|
|
|
public IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile) |
|
{ |
|
designerCodeFile = this.ViewContent.PrimaryFile; |
|
return new [] {designerCodeFile}; |
|
} |
|
|
|
public void MergeFormChanges(CodeCompileUnit unit) |
|
{ |
|
} |
|
|
|
public void NotifyFormRenamed(string newName) |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// Updates the InitializeComponent method's body with the generated code. |
|
/// </summary> |
|
public void MergeRootComponentChanges(IDesignerHost host, IDesignerSerializationManager serializationManager) |
|
{ |
|
ParseInformation parseInfo = ParseFile(); |
|
Merge(host, ViewContent.DesignerCodeFileDocument, parseInfo.CompilationUnit, textEditorOptions, serializationManager); |
|
} |
|
|
|
/// <summary> |
|
/// Merges the generated code into the specified document. |
|
/// </summary> |
|
/// <param name="component">The designer host.</param> |
|
/// <param name="document">The document that the generated code will be merged into.</param> |
|
/// <param name="parseInfo">The current compilation unit for the <paramref name="document"/>.</param> |
|
public static void Merge(IDesignerHost host, IDocument document, ICompilationUnit compilationUnit, ITextEditorOptions textEditorOptions, IDesignerSerializationManager serializationManager) |
|
{ |
|
// Get the document's initialize components method. |
|
IMethod method = GetInitializeComponents(compilationUnit); |
|
|
|
// Generate the Ruby source code. |
|
RubyCodeDomSerializer serializer = new RubyCodeDomSerializer(textEditorOptions.IndentationString); |
|
int indent = method.Region.BeginColumn; |
|
if (textEditorOptions.ConvertTabsToSpaces) { |
|
indent = (indent / textEditorOptions.IndentationSize); |
|
if (textEditorOptions.IndentationSize > 1) { |
|
indent += 1; |
|
} |
|
} |
|
string rootNamespace = GetProjectRootNamespace(compilationUnit); |
|
string methodBody = serializer.GenerateInitializeComponentMethodBody(host, serializationManager, rootNamespace, indent); |
|
|
|
// Merge the code. |
|
DomRegion methodRegion = GetBodyRegionInDocument(method); |
|
int startOffset = GetStartOffset(document, methodRegion); |
|
int endOffset = GetEndOffset(document, methodRegion); |
|
|
|
document.Replace(startOffset, endOffset - startOffset, methodBody); |
|
} |
|
|
|
/// <summary> |
|
/// Inserts an event handler. |
|
/// </summary> |
|
public bool InsertComponentEvent(IComponent component, EventDescriptor edesc, string eventMethodName, string body, out string file, out int position) |
|
{ |
|
position = GetExistingEventHandler(eventMethodName); |
|
if (position == -1) { |
|
// Ensure the text editor has the latest version |
|
// of the source code before we insert any new code. |
|
viewContent.MergeFormChanges(); |
|
|
|
// Insert the event handler at the end of the class with an extra |
|
// new line before it. |
|
IDocument doc = viewContent.DesignerCodeFileDocument; |
|
string eventHandler = CreateEventHandler(eventMethodName, body, textEditorOptions.IndentationString); |
|
|
|
IDocumentLine classEndLine = GetClassEndLine(doc); |
|
InsertEventHandlerBeforeLine(doc, eventHandler, classEndLine); |
|
|
|
// Set position so it points to the line |
|
// where the event handler was inserted. |
|
position = classEndLine.LineNumber; |
|
} |
|
|
|
// Set the filename so it refers to the form being designed. |
|
file = viewContent.DesignerCodeFile.FileName; |
|
|
|
return true; |
|
} |
|
|
|
IDocumentLine GetClassEndLine(IDocument doc) |
|
{ |
|
int line = doc.TotalNumberOfLines; |
|
while (line > 0) { |
|
IDocumentLine documentLine = doc.GetLine(line); |
|
if (documentLine.Text.Trim() == "end") { |
|
return documentLine; |
|
} |
|
line--; |
|
} |
|
return doc.GetLine(doc.TotalNumberOfLines); |
|
} |
|
|
|
void InsertEventHandlerBeforeLine(IDocument doc, string eventHandler, IDocumentLine classEndLine) |
|
{ |
|
string newContent = "\r\n" + eventHandler + "\r\n"; |
|
int offset = classEndLine.Offset; |
|
doc.Insert(offset, newContent); |
|
} |
|
|
|
/// <summary> |
|
/// Returns a list of method names that could be used as an |
|
/// event handler with the specified event. |
|
/// </summary> |
|
public ICollection GetCompatibleMethods(EventDescriptor edesc) |
|
{ |
|
// Get the form or user control class. |
|
ParseInformation parseInfo = ParseFile(); |
|
|
|
// Look at the form's methods and see which are compatible. |
|
ArrayList methods = new ArrayList(); |
|
IClass c = GetClass(parseInfo.CompilationUnit); |
|
foreach (IMethod method in c.Methods) { |
|
if (method.Parameters.Count == 2) { |
|
methods.Add(method.Name); |
|
} |
|
} |
|
|
|
return methods; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the non-generated InitializeComponents from the compilation unit. |
|
/// </summary> |
|
public static IMethod GetInitializeComponents(ICompilationUnit unit) |
|
{ |
|
foreach (IClass c in unit.Classes) { |
|
if (FormsDesignerSecondaryDisplayBinding.BaseClassIsFormOrControl(c)) { |
|
IMethod method = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(c); |
|
if (method != null) { |
|
return method; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
/// <summary> |
|
/// Converts from the DOM region to a document region. |
|
/// </summary> |
|
public static DomRegion GetBodyRegionInDocument(IMethod method) |
|
{ |
|
DomRegion bodyRegion = method.BodyRegion; |
|
return new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine, 1); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the view content attached to this generator. |
|
/// </summary> |
|
public FormsDesignerViewContent ViewContent { |
|
get { return viewContent; } |
|
} |
|
|
|
/// <summary> |
|
/// The default implementation calls the ParserService.ParseFile. This |
|
/// method is overridable so the class can be easily tested without |
|
/// the ParserService being required. |
|
/// </summary> |
|
protected virtual ParseInformation ParseFile(string fileName, string textContent) |
|
{ |
|
return ParserService.ParseFile(fileName, new StringTextBuffer(textContent)); |
|
} |
|
|
|
/// <summary> |
|
/// Returns the generated event handler. |
|
/// </summary> |
|
/// <param name="indentation">The indent string to use for the event handler.</param> |
|
protected string CreateEventHandler(string eventMethodName, string body, string indentation) |
|
{ |
|
if (String.IsNullOrEmpty(body)) { |
|
body = String.Empty; |
|
} |
|
|
|
StringBuilder eventHandler = new StringBuilder(); |
|
|
|
eventHandler.Append(indentation); |
|
eventHandler.Append("def "); |
|
eventHandler.Append(eventMethodName); |
|
eventHandler.Append("(sender, e)"); |
|
eventHandler.AppendLine(); |
|
eventHandler.Append(indentation); |
|
eventHandler.Append(textEditorOptions.IndentationString); |
|
eventHandler.Append(body); |
|
eventHandler.AppendLine(); |
|
eventHandler.Append(indentation); |
|
eventHandler.Append("end"); |
|
|
|
return eventHandler.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the form or user control class from the compilation unit. |
|
/// </summary> |
|
IClass GetClass(ICompilationUnit unit) |
|
{ |
|
return unit.Classes[0]; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the start offset of the region. |
|
/// </summary> |
|
static int GetStartOffset(IDocument document, DomRegion region) |
|
{ |
|
return document.PositionToOffset(region.BeginLine, region.BeginColumn); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the end offset of the region. |
|
/// </summary> |
|
static int GetEndOffset(IDocument document, DomRegion region) |
|
{ |
|
if (region.EndLine > document.TotalNumberOfLines) { |
|
// At end of document. |
|
return document.TextLength; |
|
} |
|
return document.PositionToOffset(region.EndLine, region.EndColumn); |
|
} |
|
|
|
/// <summary> |
|
/// Checks if the event handler already exists. |
|
/// </summary> |
|
/// <returns>The line position of the first line of the existing event handler.</returns> |
|
int GetExistingEventHandler(string methodName) |
|
{ |
|
ParseInformation parseInfo = ParseFile(); |
|
IClass c = GetClass(parseInfo.CompilationUnit); |
|
foreach (IMethod method in c.Methods) { |
|
if ((method.Name == methodName) && (method.Parameters.Count == 2)) { |
|
return method.Region.BeginLine; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
/// <summary> |
|
/// Parses the form or user control being designed. |
|
/// </summary> |
|
ParseInformation ParseFile() |
|
{ |
|
return ParseFile(this.ViewContent.DesignerCodeFile.FileName, this.ViewContent.DesignerCodeFileContent); |
|
} |
|
|
|
static string GetProjectRootNamespace(ICompilationUnit compilationUnit) |
|
{ |
|
IProject project = compilationUnit.ProjectContent.Project as IProject; |
|
if (project != null) { |
|
return project.RootNamespace; |
|
} |
|
return String.Empty; |
|
} |
|
} |
|
}
|
|
|