Browse Source

Added support for code folding of global python methods defined outside of a class.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4495 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Matt Ward 16 years ago
parent
commit
778c760b8d
  1. 69
      src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonAstWalker.cs
  2. 205
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Parsing/ParseMethodsWithNoClassTestFixture.cs
  3. 1
      src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj

69
src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonAstWalker.cs

@ -13,6 +13,7 @@ using System.IO;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using IronPython.Compiler; using IronPython.Compiler;
using IronPython.Compiler.Ast; using IronPython.Compiler.Ast;
using IronPython.Runtime;
namespace ICSharpCode.PythonBinding namespace ICSharpCode.PythonBinding
{ {
@ -23,6 +24,7 @@ namespace ICSharpCode.PythonBinding
{ {
DefaultCompilationUnit compilationUnit; DefaultCompilationUnit compilationUnit;
DefaultClass currentClass; DefaultClass currentClass;
DefaultClass globalClass;
string ns; string ns;
/// <summary> /// <summary>
@ -77,22 +79,30 @@ namespace ICSharpCode.PythonBinding
/// </summary> /// </summary>
public override bool Walk(FunctionDefinition node) public override bool Walk(FunctionDefinition node)
{ {
if (currentClass != null) { bool ignoreFirstMethodParameter = true;
string methodName = node.Name.ToString(); IClass c = currentClass;
DomRegion bodyRegion = GetBodyRegion(node.Body, node.Header); if (currentClass == null) {
DomRegion region = GetMethodRegion(node); // Walking a global method.
CreateGlobalClass();
DefaultMethod method; c = globalClass;
if (methodName == "__init__") { ignoreFirstMethodParameter = false;
method = new Constructor(ModifierEnum.Public, region, bodyRegion, currentClass); }
} else {
method = new DefaultMethod(methodName, new DefaultReturnType(currentClass), ModifierEnum.Public, region, bodyRegion, currentClass); // Create method.
} string methodName = node.Name.ToString();
foreach (IParameter parameter in ConvertParameters(node.Parameters)) { DomRegion bodyRegion = GetBodyRegion(node.Body, node.Header);
method.Parameters.Add(parameter); DomRegion region = GetMethodRegion(node);
}
currentClass.Methods.Add(method); DefaultMethod method;
if (methodName == "__init__") {
method = new Constructor(ModifierEnum.Public, region, bodyRegion, c);
} else {
method = new DefaultMethod(methodName, new DefaultReturnType(c), ModifierEnum.Public, region, bodyRegion, c);
} }
foreach (IParameter parameter in ConvertParameters(node.Parameters, ignoreFirstMethodParameter)) {
method.Parameters.Add(parameter);
}
c.Methods.Add(method);
return true; return true;
} }
@ -175,16 +185,22 @@ namespace ICSharpCode.PythonBinding
/// <summary> /// <summary>
/// Converts from Python AST expressions to parameters. /// Converts from Python AST expressions to parameters.
/// </summary> /// </summary>
IParameter[] ConvertParameters(Parameter[] parameters) /// <remarks>If the parameters belong to a class method then the first
/// "self" parameter can be ignored.</remarks>
IParameter[] ConvertParameters(Parameter[] parameters, bool ignoreFirstParameter)
{ {
List<IParameter> convertedTarameters = new List<IParameter>(); List<IParameter> convertedParameters = new List<IParameter>();
// Ignore first parameter since this is the "self" parameter. int startingIndex = 0;
for (int i = 1; i < parameters.Length; ++i) { if (ignoreFirstParameter) {
startingIndex = 1;
}
for (int i = startingIndex; i < parameters.Length; ++i) {
DefaultParameter parameter = new DefaultParameter(parameters[i].Name.ToString(), null, new DomRegion()); DefaultParameter parameter = new DefaultParameter(parameters[i].Name.ToString(), null, new DomRegion());
convertedTarameters.Add(parameter); convertedParameters.Add(parameter);
} }
return convertedTarameters.ToArray(); return convertedParameters.ToArray();
} }
@ -195,5 +211,16 @@ namespace ICSharpCode.PythonBinding
{ {
return String.Concat(ns, ".", classDef.Name.ToString()); return String.Concat(ns, ".", classDef.Name.ToString());
} }
/// <summary>
/// Creates the dummy class that is used to hold global methods.
/// </summary>
void CreateGlobalClass()
{
if (globalClass == null) {
globalClass = new DefaultClass(compilationUnit, ns);
compilationUnit.Classes.Add(globalClass);
}
}
} }
} }

205
src/AddIns/BackendBindings/Python/PythonBinding/Test/Parsing/ParseMethodsWithNoClassTestFixture.cs

@ -0,0 +1,205 @@
// <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.PythonBinding;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.TextEditor.Document;
using NUnit.Framework;
using PythonBinding.Tests;
namespace PythonBinding.Tests.Parsing
{
/// <summary>
/// Support folding when no classes are defined.
/// </summary>
[TestFixture]
public class ParseMethodsWithNoClassTestFixture
{
ICompilationUnit compilationUnit;
FoldingRegion fooFoldingRegion;
FoldingRegion barFoldingRegion;
FoldMarker fooMethodMarker;
FoldMarker barMethodMarker;
IClass globalClass;
IMethod fooMethod;
IMethod barMethod;
[TestFixtureSetUp]
public void SetUpFixture()
{
string python = "def foo():\r\n" +
"\tpass\r\n" +
"\r\n" +
"def bar(i):\r\n" +
"\tpass";
DefaultProjectContent projectContent = new DefaultProjectContent();
PythonParser parser = new PythonParser();
compilationUnit = parser.Parse(projectContent, @"C:\test.py", python);
if (compilationUnit.FoldingRegions.Count > 1) {
fooFoldingRegion = compilationUnit.FoldingRegions[0];
barFoldingRegion = compilationUnit.FoldingRegions[1];
}
if (compilationUnit.Classes.Count > 0) {
globalClass = compilationUnit.Classes[0];
if (globalClass.Methods.Count > 1) {
fooMethod = globalClass.Methods[0];
barMethod = globalClass.Methods[1];
}
}
// Get folds.
ParserFoldingStrategy foldingStrategy = new ParserFoldingStrategy();
ParseInformation parseInfo = new ParseInformation();
parseInfo.SetCompilationUnit(compilationUnit);
DocumentFactory docFactory = new DocumentFactory();
IDocument doc = docFactory.CreateDocument();
doc.TextContent = python;
List<FoldMarker> markers = foldingStrategy.GenerateFoldMarkers(doc, @"C:\Temp\test.py", parseInfo);
if (markers.Count > 1) {
fooMethodMarker = markers[0];
barMethodMarker = markers[1];
}
}
[Test]
public void OneClass()
{
Assert.AreEqual(1, compilationUnit.Classes.Count);
}
[Test]
public void GlobalClassName()
{
Assert.AreEqual("test", globalClass.Name);
}
[Test]
public void GlobalClassHasTwoMethods()
{
Assert.AreEqual(2, globalClass.Methods.Count);
}
[Test]
public void FooMethodName()
{
Assert.AreEqual("foo", fooMethod.Name);
}
[Test]
public void BarMethodName()
{
Assert.AreEqual("bar", barMethod.Name);
}
[Test]
public void FooMethodDefaultReturnType()
{
Assert.AreEqual(globalClass, fooMethod.ReturnType.GetUnderlyingClass());
}
[Test]
public void BarMethodDefaultReturnType()
{
Assert.AreEqual(globalClass, barMethod.ReturnType.GetUnderlyingClass());
}
[Test]
public void FooMethodDeclaringType()
{
Assert.AreEqual(globalClass, fooMethod.DeclaringType);
}
[Test]
public void FooMethodBodyRegion()
{
int startLine = 1;
int startColumn = 11;
int endLine = 2;
int endColumn = 6;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), fooMethod.BodyRegion.ToString());
}
/// <summary>
/// The method region needs to extend up just after the colon. It does not include the body.
/// </summary>
[Test]
public void FooMethodRegion()
{
int startLine = 1;
int startColumn = 1;
int endLine = 1;
int endColumn = 11;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), fooMethod.Region.ToString());
}
[Test]
public void BarMethodBodyRegion()
{
int startLine = 4;
int startColumn = 12;
int endLine = 5;
int endColumn = 6;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), barMethod.BodyRegion.ToString());
}
/// <summary>
/// The method region needs to extend up just after the colon. It does not include the body.
/// </summary>
[Test]
public void BarMethodRegion()
{
int startLine = 4;
int startColumn = 1;
int endLine = 4;
int endColumn = 12;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), barMethod.Region.ToString());
}
[Test]
public void BarMethodHasOneParameter()
{
Assert.AreEqual(1, barMethod.Parameters.Count);
}
[Test]
public void FooMethodFoldMarkerInnerText()
{
Assert.AreEqual("\r\n\tpass", fooMethodMarker.InnerText);
}
[Test]
public void BarMethodFoldMarkerInnerText()
{
Assert.AreEqual("\r\n\tpass", barMethodMarker.InnerText);
}
[Test]
public void FooMethodCollapsedFoldText()
{
Assert.AreEqual("...", fooMethodMarker.FoldText);
}
[Test]
public void BarMethodCollapsedFoldText()
{
Assert.AreEqual("...", barMethodMarker.FoldText);
}
}
}

1
src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj

@ -276,6 +276,7 @@
<Compile Include="Parsing\ParseClassWithMethodTestFixture.cs" /> <Compile Include="Parsing\ParseClassWithMethodTestFixture.cs" />
<Compile Include="Parsing\ParseImportTestFixture.cs" /> <Compile Include="Parsing\ParseImportTestFixture.cs" />
<Compile Include="Parsing\ParseInvalidPythonCodeTestFixture.cs" /> <Compile Include="Parsing\ParseInvalidPythonCodeTestFixture.cs" />
<Compile Include="Parsing\ParseMethodsWithNoClassTestFixture.cs" />
<Compile Include="Parsing\ParserTestFixture.cs" /> <Compile Include="Parsing\ParserTestFixture.cs" />
<Compile Include="Parsing\ParseSingleClassTestFixture.cs" /> <Compile Include="Parsing\ParseSingleClassTestFixture.cs" />
<Compile Include="Parsing\InvalidClassTestFixture.cs" /> <Compile Include="Parsing\InvalidClassTestFixture.cs" />

Loading…
Cancel
Save