Browse Source

Move IronPython and IronRuby designer generator code to common Scripting project.

pull/1/head
mrward 15 years ago
parent
commit
7bd2248f4f
  1. 4
      src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeDomSerializer.cs
  2. 256
      src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs
  3. 3
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GeneratorMergeFindsInitializeComponentsTestFixture.cs
  4. 3
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/MergeFormTestFixture.cs
  5. 3
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/NoNewLineAfterInitializeComponentTestFixture.cs
  6. 3
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/ProjectRootNamespacePassedToMergeTestFixture.cs
  7. 5
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/PythonGeneratorTestFixture.cs
  8. 4
      src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyCodeDomSerializer.cs
  9. 263
      src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyDesignerGenerator.cs
  10. 2
      src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/GeneratorMergeFindsInitializeComponentsTestFixture.cs
  11. 3
      src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/MergeFormTestFixture.cs
  12. 3
      src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/NoNewLineAfterInitializeComponentMethodTestFixture.cs
  13. 3
      src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/ProjectRootNamespacePassedToMergeTestFixture.cs
  14. 2
      src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/RubyDesignerGeneratorTestFixture.cs
  15. 7
      src/AddIns/BackendBindings/Scripting/Project/ICSharpCode.Scripting.csproj
  16. 18
      src/AddIns/BackendBindings/Scripting/Project/Src/IScriptingCodeDomSerializer.cs
  17. 276
      src/AddIns/BackendBindings/Scripting/Project/Src/ScriptingDesignerGenerator.cs
  18. 90
      src/AddIns/BackendBindings/Scripting/Test/Designer/ScriptingDesignerGeneratorTests.cs
  19. 2
      src/AddIns/BackendBindings/Scripting/Test/ICSharpCode.Scripting.Tests.csproj
  20. 8
      src/AddIns/BackendBindings/Scripting/Test/Utils/MockViewContent.cs
  21. 20
      src/AddIns/BackendBindings/Scripting/Test/Utils/TestableScriptingDesignerGenerator.cs

4
src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeDomSerializer.cs

@ -7,12 +7,14 @@ using System.ComponentModel; @@ -7,12 +7,14 @@ using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using ICSharpCode.Scripting;
namespace ICSharpCode.PythonBinding
{
/// <summary>
/// Used to generate Python code after the form has been changed in the designer.
/// </summary>
public class PythonCodeDomSerializer
public class PythonCodeDomSerializer : IScriptingCodeDomSerializer
{
PythonCodeBuilder codeBuilder;
string indentString = String.Empty;

256
src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs

@ -23,200 +23,22 @@ namespace ICSharpCode.PythonBinding @@ -23,200 +23,22 @@ namespace ICSharpCode.PythonBinding
/// <summary>
/// Form's designer generator for the Python language.
/// </summary>
public class PythonDesignerGenerator : IScriptingDesignerGenerator
public class PythonDesignerGenerator : ScriptingDesignerGenerator
{
FormsDesignerViewContent viewContent;
ITextEditorOptions textEditorOptions;
public PythonDesignerGenerator(ITextEditorOptions textEditorOptions)
{
this.textEditorOptions = textEditorOptions;
}
/// <summary>
/// Gets the Python 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)
: base(textEditorOptions)
{
}
public void NotifyFormRenamed(string newName)
public override IScriptingCodeDomSerializer CreateCodeDomSerializer(ITextEditorOptions options)
{
}
/// <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 python source code.
PythonCodeDomSerializer serializer = new PythonCodeDomSerializer(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);
int line = doc.TotalNumberOfLines;
IDocumentLine lastLineSegment = doc.GetLine(line);
int offset = lastLineSegment.Offset + lastLineSegment.Length;
string newContent = "\r\n" + eventHandler;
if (lastLineSegment.Length > 0) {
// Add an extra new line between the last line and the event handler.
newContent = "\r\n" + newContent;
}
doc.Insert(offset, newContent);
// Set position so it points to the line
// where the event handler was inserted.
position = line + 1;
}
// Set the filename so it refers to the form being designed.
file = viewContent.DesignerCodeFile.FileName;
return true;
}
/// <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 parse info.
/// </summary>
public static IMethod GetInitializeComponents(ParseInformation parseInfo)
{
return GetInitializeComponents(parseInfo.CompilationUnit);
}
/// <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, 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));
return new PythonCodeDomSerializer(options.IndentationString);
}
/// <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)
public override string CreateEventHandler(string eventMethodName, string body, string indentation)
{
if (String.IsNullOrEmpty(body)) {
body = "pass";
@ -230,71 +52,37 @@ namespace ICSharpCode.PythonBinding @@ -230,71 +52,37 @@ namespace ICSharpCode.PythonBinding
eventHandler.Append("(self, sender, e):");
eventHandler.AppendLine();
eventHandler.Append(indentation);
eventHandler.Append(textEditorOptions.IndentationString);
eventHandler.Append(TextEditorOptions.IndentationString);
eventHandler.Append(body);
return eventHandler.ToString();
}
/// <summary>
/// Gets the form or user control class from the compilation unit.
/// Converts from the DOM region to a document region.
/// </summary>
IClass GetClass(ICompilationUnit unit)
public override DomRegion GetBodyRegionInDocument(IMethod method)
{
return unit.Classes[0];
DomRegion bodyRegion = method.BodyRegion;
return new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine + 1, 1);
}
/// <summary>
/// Gets the start offset of the region.
/// </summary>
static int GetStartOffset(IDocument document, DomRegion region)
public override int InsertEventHandler(IDocument document, string eventHandler)
{
return document.PositionToOffset(region.BeginLine, region.BeginColumn);
}
int line = document.TotalNumberOfLines;
IDocumentLine lastLineSegment = document.GetLine(line);
int offset = lastLineSegment.Offset + lastLineSegment.Length;
/// <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;
string newContent = "\r\n" + eventHandler;
if (lastLineSegment.Length > 0) {
// Add an extra new line between the last line and the event handler.
newContent = "\r\n" + newContent;
}
return document.PositionToOffset(region.EndLine, region.EndColumn);
}
document.Insert(offset, newContent);
/// <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;
// Set position so it points to the line
// where the event handler was inserted.
return line + 1;
}
}
}

3
src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GeneratorMergeFindsInitializeComponentsTestFixture.cs

@ -73,7 +73,8 @@ namespace PythonBinding.Tests.Designer @@ -73,7 +73,8 @@ namespace PythonBinding.Tests.Designer
method.BodyRegion = bodyRegion;
DomRegion expectedRegion = new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine + 1, 1);
Assert.AreEqual(expectedRegion, PythonDesignerGenerator.GetBodyRegionInDocument(method));
PythonDesignerGenerator generator = new PythonDesignerGenerator(new MockTextEditorOptions());
Assert.AreEqual(expectedRegion, generator.GetBodyRegionInDocument(method));
}
[Test]

3
src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/MergeFormTestFixture.cs

@ -60,7 +60,8 @@ namespace PythonBinding.Tests.Designer @@ -60,7 +60,8 @@ namespace PythonBinding.Tests.Designer
using (serializationManager.CreateSession()) {
AvalonEditDocumentAdapter adapter = new AvalonEditDocumentAdapter(document, null);
MockTextEditorOptions options = new MockTextEditorOptions();
PythonDesignerGenerator.Merge(host, adapter, compilationUnit, options, serializationManager);
PythonDesignerGenerator generator = new PythonDesignerGenerator(options);
generator.Merge(host, adapter, compilationUnit, serializationManager);
}
}
}

3
src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/NoNewLineAfterInitializeComponentTestFixture.cs

@ -54,7 +54,8 @@ namespace PythonBinding.Tests.Designer @@ -54,7 +54,8 @@ namespace PythonBinding.Tests.Designer
using (serializationManager.CreateSession()) {
AvalonEditDocumentAdapter adapter = new AvalonEditDocumentAdapter(document, null);
MockTextEditorOptions options = new MockTextEditorOptions();
PythonDesignerGenerator.Merge(host, adapter, compilationUnit, options, serializationManager);
PythonDesignerGenerator generator = new PythonDesignerGenerator(options);
generator.Merge(host, adapter, compilationUnit, serializationManager);
}
}
}

3
src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/ProjectRootNamespacePassedToMergeTestFixture.cs

@ -70,7 +70,8 @@ namespace PythonBinding.Tests.Designer @@ -70,7 +70,8 @@ namespace PythonBinding.Tests.Designer
DesignerSerializationManager serializationManager = new DesignerSerializationManager(host);
using (serializationManager.CreateSession()) {
AvalonEditDocumentAdapter adapter = new AvalonEditDocumentAdapter(document, null);
PythonDesignerGenerator.Merge(host, adapter, compilationUnit, options, serializationManager);
PythonDesignerGenerator generator = new PythonDesignerGenerator(options);
generator.Merge(host, adapter, compilationUnit, serializationManager);
}
}
}

5
src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/PythonGeneratorTestFixture.cs

@ -7,8 +7,9 @@ using System.CodeDom.Compiler; @@ -7,8 +7,9 @@ using System.CodeDom.Compiler;
using System.ComponentModel;
using System.IO;
using ICSharpCode.PythonBinding;
using ICSharpCode.FormsDesigner;
using ICSharpCode.PythonBinding;
using ICSharpCode.Scripting.Tests.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using NUnit.Framework;
@ -29,7 +30,7 @@ namespace PythonBinding.Tests.Designer @@ -29,7 +30,7 @@ namespace PythonBinding.Tests.Designer
DomRegion expectedRegion = new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine + 1, 1);
DerivedPythonDesignerGenerator generator = new DerivedPythonDesignerGenerator();
Assert.AreEqual(expectedRegion, PythonDesignerGenerator.GetBodyRegionInDocument(method));
Assert.AreEqual(expectedRegion, generator.GetBodyRegionInDocument(method));
}
[Test]

4
src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyCodeDomSerializer.cs

@ -8,12 +8,14 @@ using System.ComponentModel.Design; @@ -8,12 +8,14 @@ using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Text;
using ICSharpCode.Scripting;
namespace ICSharpCode.RubyBinding
{
/// <summary>
/// Used to generate Ruby code after the form has been changed in the designer.
/// </summary>
public class RubyCodeDomSerializer
public class RubyCodeDomSerializer : IScriptingCodeDomSerializer
{
RubyCodeBuilder codeBuilder;

263
src/AddIns/BackendBindings/Ruby/RubyBinding/Project/Src/RubyDesignerGenerator.cs

@ -23,205 +23,22 @@ namespace ICSharpCode.RubyBinding @@ -23,205 +23,22 @@ namespace ICSharpCode.RubyBinding
/// <summary>
/// Form's designer generator for the Ruby language.
/// </summary>
public class RubyDesignerGenerator : IScriptingDesignerGenerator
public class RubyDesignerGenerator : ScriptingDesignerGenerator
{
FormsDesignerViewContent viewContent;
ITextEditorOptions textEditorOptions;
public RubyDesignerGenerator(ITextEditorOptions textEditorOptions)
: base(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)
public override IScriptingCodeDomSerializer CreateCodeDomSerializer(ITextEditorOptions options)
{
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));
return new RubyCodeDomSerializer(options.IndentationString);
}
/// <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)
public override string CreateEventHandler(string eventMethodName, string body, string indentation)
{
if (String.IsNullOrEmpty(body)) {
body = String.Empty;
@ -235,7 +52,7 @@ namespace ICSharpCode.RubyBinding @@ -235,7 +52,7 @@ namespace ICSharpCode.RubyBinding
eventHandler.Append("(sender, e)");
eventHandler.AppendLine();
eventHandler.Append(indentation);
eventHandler.Append(textEditorOptions.IndentationString);
eventHandler.Append(TextEditorOptions.IndentationString);
eventHandler.Append(body);
eventHandler.AppendLine();
eventHandler.Append(indentation);
@ -245,64 +62,46 @@ namespace ICSharpCode.RubyBinding @@ -245,64 +62,46 @@ namespace ICSharpCode.RubyBinding
}
/// <summary>
/// Gets the form or user control class from the compilation unit.
/// Insert the event handler at the end of the class with an extra
/// new line before it.
/// </summary>
IClass GetClass(ICompilationUnit unit)
public override int InsertEventHandler(IDocument document, string eventHandler)
{
return unit.Classes[0];
}
IDocumentLine classEndLine = GetClassEndLine(document);
InsertEventHandlerBeforeLine(document, eventHandler, classEndLine);
/// <summary>
/// Gets the start offset of the region.
/// </summary>
static int GetStartOffset(IDocument document, DomRegion region)
{
return document.PositionToOffset(region.BeginLine, region.BeginColumn);
// Set position so it points to the line
// where the event handler was inserted.
return classEndLine.LineNumber;
}
/// <summary>
/// Gets the end offset of the region.
/// </summary>
static int GetEndOffset(IDocument document, DomRegion region)
IDocumentLine GetClassEndLine(IDocument doc)
{
if (region.EndLine > document.TotalNumberOfLines) {
// At end of document.
return document.TextLength;
int line = doc.TotalNumberOfLines;
while (line > 0) {
IDocumentLine documentLine = doc.GetLine(line);
if (documentLine.Text.Trim() == "end") {
return documentLine;
}
line--;
}
return document.PositionToOffset(region.EndLine, region.EndColumn);
return doc.GetLine(doc.TotalNumberOfLines);
}
/// <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)
void InsertEventHandlerBeforeLine(IDocument doc, string eventHandler, IDocumentLine classEndLine)
{
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;
string newContent = "\r\n" + eventHandler + "\r\n";
int offset = classEndLine.Offset;
doc.Insert(offset, newContent);
}
/// <summary>
/// Parses the form or user control being designed.
/// Converts from the DOM region to a document region.
/// </summary>
ParseInformation ParseFile()
{
return ParseFile(this.ViewContent.DesignerCodeFile.FileName, this.ViewContent.DesignerCodeFileContent);
}
static string GetProjectRootNamespace(ICompilationUnit compilationUnit)
public override DomRegion GetBodyRegionInDocument(IMethod method)
{
IProject project = compilationUnit.ProjectContent.Project as IProject;
if (project != null) {
return project.RootNamespace;
}
return String.Empty;
DomRegion bodyRegion = method.BodyRegion;
return new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine, 1);
}
}
}

2
src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/GeneratorMergeFindsInitializeComponentsTestFixture.cs

@ -73,7 +73,7 @@ namespace RubyBinding.Tests.Designer @@ -73,7 +73,7 @@ namespace RubyBinding.Tests.Designer
method.BodyRegion = bodyRegion;
DomRegion expectedRegion = new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine, 1);
Assert.AreEqual(expectedRegion, RubyDesignerGenerator.GetBodyRegionInDocument(method));
Assert.AreEqual(expectedRegion, generator.GetBodyRegionInDocument(method));
}
[Test]

3
src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/MergeFormTestFixture.cs

@ -58,7 +58,8 @@ namespace RubyBinding.Tests.Designer @@ -58,7 +58,8 @@ namespace RubyBinding.Tests.Designer
DesignerSerializationManager serializationManager = new DesignerSerializationManager(host);
using (serializationManager.CreateSession()) {
RubyDesignerGenerator.Merge(host, new AvalonEditDocumentAdapter(document, null), compilationUnit, new MockTextEditorOptions(), serializationManager);
RubyDesignerGenerator generator = new RubyDesignerGenerator(new MockTextEditorOptions());
generator.Merge(host, new AvalonEditDocumentAdapter(document, null), compilationUnit, serializationManager);
}
}
}

3
src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/NoNewLineAfterInitializeComponentMethodTestFixture.cs

@ -53,7 +53,8 @@ namespace RubyBinding.Tests.Designer @@ -53,7 +53,8 @@ namespace RubyBinding.Tests.Designer
DesignerSerializationManager serializationManager = new DesignerSerializationManager(host);
using (serializationManager.CreateSession()) {
AvalonEditDocumentAdapter docAdapter = new AvalonEditDocumentAdapter(document, null);
RubyDesignerGenerator.Merge(host, docAdapter, compilationUnit, new MockTextEditorOptions(), serializationManager);
RubyDesignerGenerator generator = new RubyDesignerGenerator(new MockTextEditorOptions());
generator.Merge(host, docAdapter, compilationUnit, serializationManager);
}
}
}

3
src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/ProjectRootNamespacePassedToMergeTestFixture.cs

@ -70,7 +70,8 @@ namespace RubyBinding.Tests.Designer @@ -70,7 +70,8 @@ namespace RubyBinding.Tests.Designer
DesignerSerializationManager serializationManager = new DesignerSerializationManager(host);
using (serializationManager.CreateSession()) {
AvalonEditDocumentAdapter docAdapter = new AvalonEditDocumentAdapter(document, null);
RubyDesignerGenerator.Merge(host, docAdapter, compilationUnit, options, serializationManager);
RubyDesignerGenerator generator = new RubyDesignerGenerator(options);
generator.Merge(host, docAdapter, compilationUnit, serializationManager);
}
}
}

2
src/AddIns/BackendBindings/Ruby/RubyBinding/Test/Designer/RubyDesignerGeneratorTestFixture.cs

@ -29,7 +29,7 @@ namespace RubyBinding.Tests.Designer @@ -29,7 +29,7 @@ namespace RubyBinding.Tests.Designer
DomRegion expectedRegion = new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine, 1);
DerivedRubyDesignerGenerator generator = new DerivedRubyDesignerGenerator();
Assert.AreEqual(expectedRegion, RubyDesignerGenerator.GetBodyRegionInDocument(method));
Assert.AreEqual(expectedRegion, generator.GetBodyRegionInDocument(method));
}
[Test]

7
src/AddIns/BackendBindings/Scripting/Project/ICSharpCode.Scripting.csproj

@ -71,6 +71,7 @@ @@ -71,6 +71,7 @@
<Compile Include="Src\IControlDispatcher.cs" />
<Compile Include="Src\ILock.cs" />
<Compile Include="Src\IMemberProvider.cs" />
<Compile Include="Src\IScriptingCodeDomSerializer.cs" />
<Compile Include="Src\IScriptingConsole.cs" />
<Compile Include="Src\IScriptingConsolePad.cs" />
<Compile Include="Src\IScriptingConsoleTextEditor.cs" />
@ -83,6 +84,7 @@ @@ -83,6 +84,7 @@
<Compile Include="Src\ScriptingConsoleOutputStream.cs" />
<Compile Include="Src\ScriptingConsoleTextEditor.cs" />
<Compile Include="Src\ScriptingConsoleTextEditorKeyEventArgs.cs" />
<Compile Include="Src\ScriptingDesignerGenerator.cs" />
<Compile Include="Src\ScriptingDesignerLoader.cs" />
<Compile Include="Src\ScriptingFileService.cs" />
<Compile Include="Src\ScriptingNameCreationService.cs" />
@ -107,6 +109,11 @@ @@ -107,6 +109,11 @@
<Name>ICSharpCode.Core</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj">
<Project>{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}</Project>
<Name>ICSharpCode.SharpDevelop.Dom</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Analysis\UnitTesting\UnitTesting.csproj">
<Project>{1F261725-6318-4434-A1B1-6C70CE4CD324}</Project>
<Name>UnitTesting</Name>

18
src/AddIns/BackendBindings/Scripting/Project/Src/IScriptingCodeDomSerializer.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
// <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.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
namespace ICSharpCode.Scripting
{
public interface IScriptingCodeDomSerializer
{
string GenerateInitializeComponentMethodBody(IDesignerHost host, IDesignerSerializationManager serializationManager, string rootNamespace, int initialIndent);
}
}

276
src/AddIns/BackendBindings/Scripting/Project/Src/ScriptingDesignerGenerator.cs

@ -0,0 +1,276 @@ @@ -0,0 +1,276 @@
// <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 ICSharpCode.FormsDesigner;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.Scripting
{
public abstract class ScriptingDesignerGenerator : IScriptingDesignerGenerator
{
FormsDesignerViewContent viewContent;
ITextEditorOptions textEditorOptions;
public ITextEditorOptions TextEditorOptions {
get { return textEditorOptions; }
}
public ScriptingDesignerGenerator(ITextEditorOptions textEditorOptions)
{
this.textEditorOptions = textEditorOptions;
}
public CodeDomProvider CodeDomProvider {
get { return null; }
}
public FormsDesignerViewContent ViewContent {
get { return viewContent; }
}
public void Attach(FormsDesignerViewContent viewContent)
{
this.viewContent = viewContent;
}
public void Detach()
{
this.viewContent = null;
}
public IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile)
{
designerCodeFile = viewContent.PrimaryFile;
return new [] {designerCodeFile};
}
public void MergeFormChanges(CodeCompileUnit unit)
{
}
/// <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;
}
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, serializationManager);
}
/// <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>
/// 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 void Merge(IDesignerHost host, IDocument document, ICompilationUnit compilationUnit, IDesignerSerializationManager serializationManager)
{
IMethod method = GetInitializeComponents(compilationUnit);
// Generate the python source code.
IScriptingCodeDomSerializer serializer = CreateCodeDomSerializer(TextEditorOptions);
int indent = GetIndent(method);
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);
}
public virtual IScriptingCodeDomSerializer CreateCodeDomSerializer(ITextEditorOptions options)
{
return null;
}
public int GetIndent(IMethod method)
{
int indent = method.Region.BeginColumn;
if (textEditorOptions.ConvertTabsToSpaces) {
indent = (indent / textEditorOptions.IndentationSize);
if (textEditorOptions.IndentationSize > 1) {
indent += 1;
}
}
return indent;
}
public virtual DomRegion GetBodyRegionInDocument(IMethod method)
{
return DomRegion.Empty;
}
/// <summary>
/// Gets the non-generated InitializeComponents from parse info.
/// </summary>
public static IMethod GetInitializeComponents(ParseInformation parseInfo)
{
return GetInitializeComponents(parseInfo.CompilationUnit);
}
/// <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>
/// 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>
/// 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);
position = InsertEventHandler(doc, eventHandler);
}
// Set the filename so it refers to the form being designed.
file = ViewContent.DesignerCodeFile.FileName;
return true;
}
/// <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;
}
public virtual string CreateEventHandler(string eventMethodName, string body, string indentation)
{
return String.Empty;
}
public virtual int InsertEventHandler(IDocument document, string eventHandler)
{
return 0;
}
/// <summary>
/// Parses the form or user control being designed.
/// </summary>
ParseInformation ParseFile()
{
return ParseFile(this.ViewContent.DesignerCodeFile.FileName, this.ViewContent.DesignerCodeFileContent);
}
string GetProjectRootNamespace(ICompilationUnit compilationUnit)
{
IProject project = compilationUnit.ProjectContent.Project as IProject;
if (project != null) {
return project.RootNamespace;
}
return String.Empty;
}
}
}

90
src/AddIns/BackendBindings/Scripting/Test/Designer/ScriptingDesignerGeneratorTests.cs

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
// <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.Collections.Generic;
using ICSharpCode.FormsDesigner;
using ICSharpCode.Scripting;
using ICSharpCode.Scripting.Tests.Utils;
using ICSharpCode.SharpDevelop;
using NUnit.Framework;
namespace ICSharpCode.Scripting.Tests.Designer
{
[TestFixture]
[RequiresSTA]
public class ScriptingDesignerGeneratorTests
{
MockTextEditorOptions textEditorOptions;
TestableScriptingDesignerGenerator generator;
FormsDesignerViewContent formsDesignerView;
MockTextEditorViewContent textEditorViewContent;
MockOpenedFile formsDesignerOpenedFile;
[Test]
public void GetSourceFiles_FormDesignerViewHasOpenFile_ReturnsOneFile()
{
CreateDesignerGenerator();
OpenedFile designerOpenedFile;
IEnumerable<OpenedFile> files = generator.GetSourceFiles(out designerOpenedFile);
int count = HowManyInCollection(files);
Assert.AreEqual(1, count);
}
void CreateDesignerGenerator()
{
textEditorViewContent = new MockTextEditorViewContent();
formsDesignerOpenedFile = new MockOpenedFile("test.py");
textEditorViewContent.PrimaryFile = formsDesignerOpenedFile;
formsDesignerView = new FormsDesignerViewContent(textEditorViewContent, formsDesignerOpenedFile);
textEditorOptions = new MockTextEditorOptions();
generator = new TestableScriptingDesignerGenerator(textEditorOptions);
generator.Attach(formsDesignerView);
}
int HowManyInCollection(IEnumerable<OpenedFile> files)
{
int count = 0;
foreach (OpenedFile file in files) {
count++;
}
return count;
}
[Test]
public void GetSourceFiles_FormsDesignerHasOpenFile_DesignerOpenedFileParameterIsSetToFormsDesignerViewOpenedFile()
{
CreateDesignerGenerator();
OpenedFile designerOpenedFile;
generator.GetSourceFiles(out designerOpenedFile);
AssertAreEqual(formsDesignerOpenedFile, designerOpenedFile);
}
void AssertAreEqual(OpenedFile expectedOpenedFile, OpenedFile actualOpenedFile)
{
string fileName = actualOpenedFile.FileName.ToString();
string expectedFileName = expectedOpenedFile.FileName.ToString();
Assert.AreEqual(expectedFileName, fileName);
}
[Test]
public void GetSourceFiles_FormsDesignerHasOpenFile_FormsDesignerFileReturnedInFiles()
{
CreateDesignerGenerator();
OpenedFile designerOpenedFile;
IEnumerable<OpenedFile> files = generator.GetSourceFiles(out designerOpenedFile);
IEnumerator<OpenedFile> enumerator = files.GetEnumerator();
enumerator.MoveNext();
OpenedFile file = enumerator.Current;
AssertAreEqual(formsDesignerOpenedFile, file);
}
}
}

2
src/AddIns/BackendBindings/Scripting/Test/ICSharpCode.Scripting.Tests.csproj

@ -77,6 +77,7 @@ @@ -77,6 +77,7 @@
<Compile Include="Console\ScriptingConsoleTextEditorTests.cs" />
<Compile Include="Console\ThreadSafeScriptingConsoleTextEditorTests.cs" />
<Compile Include="Designer\NameCreationServiceTests.cs" />
<Compile Include="Designer\ScriptingDesignerGeneratorTests.cs" />
<Compile Include="Designer\ScriptingCodeBuilderTests.cs" />
<Compile Include="Designer\ScriptingDesignerLoaderGetResourcesTests.cs" />
<Compile Include="Designer\ScriptingDesignerLoaderTests.cs" />
@ -132,6 +133,7 @@ @@ -132,6 +133,7 @@
<Compile Include="Utils\MSBuildEngineHelper.cs" />
<Compile Include="Utils\NullPropertyUserControl.cs" />
<Compile Include="Utils\SupportInitCustomControl.cs" />
<Compile Include="Utils\TestableScriptingDesignerGenerator.cs" />
<Compile Include="Utils\TestableScriptingDesignerLoader.cs" />
<Compile Include="Utils\Tests\AddInPathHelperTests.cs" />
<Compile Include="Utils\Tests\MockComponentCreatorTests.cs" />

8
src/AddIns/BackendBindings/Scripting/Test/Utils/MockViewContent.cs

@ -13,6 +13,7 @@ namespace ICSharpCode.Scripting.Tests.Utils @@ -13,6 +13,7 @@ namespace ICSharpCode.Scripting.Tests.Utils
{
public class MockViewContent : IViewContent
{
OpenedFile primaryFile = new MockOpenedFile();
FileName fileName;
List<IViewContent> secondaryViewContents = new List<IViewContent>();
@ -143,11 +144,8 @@ namespace ICSharpCode.Scripting.Tests.Utils @@ -143,11 +144,8 @@ namespace ICSharpCode.Scripting.Tests.Utils
}
public OpenedFile PrimaryFile {
get {
MockOpenedFile file = new MockOpenedFile();
//file.FileName = fileName;
return file;
}
get { return primaryFile; }
set { primaryFile = value; }
}
public FileName PrimaryFileName {

20
src/AddIns/BackendBindings/Scripting/Test/Utils/TestableScriptingDesignerGenerator.cs

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
// <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 ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.Scripting.Tests.Utils
{
public class TestableScriptingDesignerGenerator : ScriptingDesignerGenerator
{
public TestableScriptingDesignerGenerator(ITextEditorOptions textEditorOptions)
: base(textEditorOptions)
{
}
}
}
Loading…
Cancel
Save