diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/JavaScriptBinding.csproj b/src/AddIns/BackendBindings/JavaScriptBinding/Project/JavaScriptBinding.csproj
index 94b53c1c27..e94962270b 100644
--- a/src/AddIns/BackendBindings/JavaScriptBinding/Project/JavaScriptBinding.csproj
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/JavaScriptBinding.csproj
@@ -57,11 +57,15 @@
+
+
+
+
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/ITokenExtensions.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/ITokenExtensions.cs
index 0e489f2b19..abd1acbef6 100644
--- a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/ITokenExtensions.cs
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/ITokenExtensions.cs
@@ -3,6 +3,7 @@
using System;
using Antlr.Runtime;
+using Xebic.Parsers.ES3;
namespace ICSharpCode.JavaScriptBinding
{
@@ -17,5 +18,10 @@ namespace ICSharpCode.JavaScriptBinding
{
return token.CharPositionInLine + token.Text.Length + 1;
}
+
+ public static bool IsSingleLineComment(this IToken token)
+ {
+ return token.Type == ES3Lexer.SingleLineComment;
+ }
}
}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAst.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAst.cs
index 0a61b2369c..c8fc5f39c8 100644
--- a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAst.cs
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAst.cs
@@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
+using System.Collections.Generic;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
@@ -30,5 +31,10 @@ namespace ICSharpCode.JavaScriptBinding
{
return tokenStream.Get(index);
}
+
+ public IList GetTokens()
+ {
+ return tokenStream.GetTokens();
+ }
}
}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAstWalker.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAstWalker.cs
index 7fd0908d80..e05aee7df6 100644
--- a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAstWalker.cs
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptAstWalker.cs
@@ -26,6 +26,7 @@ namespace ICSharpCode.JavaScriptBinding
{
if (ast.IsValid) {
Walk(ast.Tree);
+ WalkRegions();
}
}
@@ -52,5 +53,11 @@ namespace ICSharpCode.JavaScriptBinding
Walk(child);
}
}
+
+ void WalkRegions()
+ {
+ var regionWalker = new JavaScriptRegionWalker(ast, compilationUnit);
+ regionWalker.Walk();
+ }
}
}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegion.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegion.cs
new file mode 100644
index 0000000000..adb9dddf15
--- /dev/null
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegion.cs
@@ -0,0 +1,42 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using ICSharpCode.SharpDevelop.Dom;
+
+namespace ICSharpCode.JavaScriptBinding
+{
+ public class JavaScriptRegion
+ {
+ JavaScriptRegionStart start;
+ JavaScriptRegionEnd end;
+
+ public JavaScriptRegion(JavaScriptRegionStart start, JavaScriptRegionEnd end)
+ {
+ this.start = start;
+ this.end = end;
+ }
+
+ public void AddRegion(IList foldingRegions)
+ {
+ FoldingRegion namedFoldingRegion = CreateFoldingRegion();
+ foldingRegions.Add(namedFoldingRegion);
+ }
+
+ FoldingRegion CreateFoldingRegion()
+ {
+ DomRegion location = GetRegionLocation();
+ return new FoldingRegion(start.Name, location);
+ }
+
+ DomRegion GetRegionLocation()
+ {
+ int beginLine = start.Line;
+ int endLine = end.Line;
+ int beginColumn = start.StartColumn;
+ int endColumn = end.EndColumn;
+ return new DomRegion(beginLine, beginColumn, endLine, endColumn);
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionEnd.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionEnd.cs
new file mode 100644
index 0000000000..7059416d6e
--- /dev/null
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionEnd.cs
@@ -0,0 +1,34 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Antlr.Runtime;
+
+namespace ICSharpCode.JavaScriptBinding
+{
+ public class JavaScriptRegionEnd
+ {
+ public static readonly string RegionEndText = "//#endregion";
+ public static readonly int RegionEndTextLength = RegionEndText.Length;
+
+ IToken token;
+
+ public JavaScriptRegionEnd(IToken token)
+ {
+ this.token = token;
+ }
+
+ public static bool IsRegionEnd(IToken token)
+ {
+ return token.Text.StartsWith(RegionEndText);
+ }
+
+ public int Line {
+ get { return token.Line; }
+ }
+
+ public int EndColumn {
+ get { return token.BeginColumn() + RegionEndTextLength; }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionStart.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionStart.cs
new file mode 100644
index 0000000000..2fce79b57b
--- /dev/null
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionStart.cs
@@ -0,0 +1,51 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Antlr.Runtime;
+
+namespace ICSharpCode.JavaScriptBinding
+{
+ public class JavaScriptRegionStart
+ {
+ public static readonly string RegionStartText = "//#region ";
+ public static readonly int RegionStartTextLength = RegionStartText.Length;
+
+ IToken token;
+ string name;
+
+ public JavaScriptRegionStart(IToken token)
+ {
+ this.token = token;
+ }
+
+ public string Name {
+ get {
+ if (name == null) {
+ GetName();
+ }
+ return name;
+ }
+ }
+
+ void GetName()
+ {
+ string text = token.Text;
+ int index = text.IndexOf(RegionStartText);
+ name = text.Substring(index + RegionStartTextLength);
+ }
+
+ public static bool IsRegionStart(IToken token)
+ {
+ return token.Text.StartsWith(RegionStartText);
+ }
+
+ public int Line {
+ get { return token.Line; }
+ }
+
+ public int StartColumn {
+ get { return token.BeginColumn(); }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionWalker.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionWalker.cs
new file mode 100644
index 0000000000..7c17023b3a
--- /dev/null
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Project/Src/JavaScriptRegionWalker.cs
@@ -0,0 +1,64 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Antlr.Runtime;
+using Antlr.Runtime.Tree;
+using ICSharpCode.SharpDevelop.Dom;
+using Xebic.Parsers.ES3;
+
+namespace ICSharpCode.JavaScriptBinding
+{
+ public class JavaScriptRegionWalker
+ {
+ JavaScriptAst ast;
+ ICompilationUnit compilationUnit;
+ Stack regions;
+
+ public JavaScriptRegionWalker(
+ JavaScriptAst ast,
+ ICompilationUnit compilationUnit)
+ {
+ this.ast = ast;
+ this.compilationUnit = compilationUnit;
+ }
+
+ public void Walk()
+ {
+ regions = new Stack();
+
+ foreach (IToken token in ast.GetTokens()) {
+ if (token.IsSingleLineComment()) {
+ WalkComment(token);
+ }
+ }
+ }
+
+ void WalkComment(IToken token)
+ {
+ if (JavaScriptRegionStart.IsRegionStart(token)) {
+ WalkRegionStart(token);
+ } else if (JavaScriptRegionEnd.IsRegionEnd(token)) {
+ WalkRegionEnd(token);
+ }
+ }
+
+ void WalkRegionStart(IToken token)
+ {
+ var regionStart = new JavaScriptRegionStart(token);
+ regions.Push(regionStart);
+ }
+
+ void WalkRegionEnd(IToken token)
+ {
+ if (regions.Count > 0) {
+ JavaScriptRegionStart regionStart = regions.Pop();
+
+ var regionEnd = new JavaScriptRegionEnd(token);
+ var region = new JavaScriptRegion(regionStart, regionEnd);
+ region.AddRegion(compilationUnit.FoldingRegions);
+ }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/JavaScriptBinding/Test/Src/JavaScriptParserTests.cs b/src/AddIns/BackendBindings/JavaScriptBinding/Test/Src/JavaScriptParserTests.cs
index df12865e73..9e8ecf95b6 100644
--- a/src/AddIns/BackendBindings/JavaScriptBinding/Test/Src/JavaScriptParserTests.cs
+++ b/src/AddIns/BackendBindings/JavaScriptBinding/Test/Src/JavaScriptParserTests.cs
@@ -66,6 +66,14 @@ namespace JavaScriptBinding.Tests
get { return FirstClass.Methods[0]; }
}
+ FoldingRegion FirstRegion {
+ get { return compilationUnit.FoldingRegions[0]; }
+ }
+
+ FoldingRegion SecondRegion {
+ get { return compilationUnit.FoldingRegions[1]; }
+ }
+
[Test]
public void CanParse_CSharpProjectPassed_ReturnsTrue()
{
@@ -279,5 +287,221 @@ namespace JavaScriptBinding.Tests
Assert.AreEqual(expectedRegion, methodBodyRegion);
}
+
+ [Test]
+ public void Parse_JavaScriptCodeHasOneRegion_OneFoldingRegionAddedToCompilationUnit()
+ {
+ string code =
+ "//#region MyRegion\r\n" +
+ "var a = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ int count = compilationUnit.FoldingRegions.Count;
+
+ Assert.AreEqual(1, count);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasOneRegionWithName_FoldingRegionAddedWithName()
+ {
+ string code =
+ "//#region MyRegion\r\n" +
+ "var a = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ string name = FirstRegion.Name;
+
+ Assert.AreEqual("MyRegion", name);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasNoRegions_NoFoldingRegionsAddedToCompilationUnit()
+ {
+ string code = "var a = 1;";
+
+ ParseJavaScript(code);
+
+ int count = compilationUnit.FoldingRegions.Count;
+
+ Assert.AreEqual(0, count);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeSingleCommentEndsWithRegionTextButDoesNotStartWithRegion_NoRegionsAddedToCompilationUnit()
+ {
+ string code =
+ "//not a #region test\r\n" +
+ "var a = 1;\r\n" +
+ "//not an #endregion\r\n";
+
+ ParseJavaScript(code);
+
+ Assert.AreEqual(0, compilationUnit.FoldingRegions.Count);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasOneRegion_RegionLocationIsSpecified()
+ {
+ string code =
+ "//#region MyRegion\r\n" +
+ "var a = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ DomRegion location = FirstRegion.Region;
+
+ int beginLine = 1;
+ int endLine = 3;
+ int beginColumn = 1;
+ int endColumn = 13;
+
+ var expectedLocation = new DomRegion(beginLine, beginColumn, endLine, endColumn);
+
+ Assert.AreEqual(expectedLocation, location);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasRegionStartOnly_NoRegionsAddedToCompilationUnit()
+ {
+ string code =
+ "//#region MyRegion\r\n" +
+ "var a = 1;\r\n";
+
+ ParseJavaScript(code);
+
+ Assert.AreEqual(0, compilationUnit.FoldingRegions.Count);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasOneRegionWithEndRegionFollowedByText_TextFollowingEndRegionIsNotPartOfFold()
+ {
+ string code =
+ "//#region MyRegion\r\n" +
+ "var a = 1;\r\n" +
+ "//#endregion abc\r\n";
+
+ ParseJavaScript(code);
+
+ DomRegion location = FirstRegion.Region;
+
+ int beginLine = 1;
+ int endLine = 3;
+ int beginColumn = 1;
+ int endColumn = 13;
+
+ var expectedLocation = new DomRegion(beginLine, beginColumn, endLine, endColumn);
+
+ Assert.AreEqual(expectedLocation, location);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasTwoRegions_TwoRegionsAddedToCompilationUnit()
+ {
+ string code =
+ "//#region One\r\n" +
+ "var a = 1;\r\n" +
+ "//#endregion\r\n" +
+ "\r\n" +
+ "//#region Two\r\n" +
+ "var b = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ Assert.AreEqual(2, compilationUnit.FoldingRegions.Count);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasTwoRegions_RegionsHaveCorrectLocationsAndNames()
+ {
+ string code =
+ "//#region One\r\n" +
+ "var a = 1;\r\n" +
+ "//#endregion\r\n" +
+ "\r\n" +
+ "//#region Two\r\n" +
+ "var b = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ DomRegion firstRegionLocation = FirstRegion.Region;
+
+ int beginLine = 1;
+ int endLine = 3;
+ int beginColumn = 1;
+ int endColumn = 13;
+
+ var expectedFirstRegionLocation = new DomRegion(beginLine, beginColumn, endLine, endColumn);
+
+ DomRegion secondRegionLocation = SecondRegion.Region;
+
+ beginLine = 5;
+ endLine = 7;
+ beginColumn = 1;
+ endColumn = 13;
+
+ var expectedSecondRegionLocation = new DomRegion(beginLine, beginColumn, endLine, endColumn);
+
+ Assert.AreEqual(expectedFirstRegionLocation, firstRegionLocation);
+ Assert.AreEqual("One", FirstRegion.Name);
+ Assert.AreEqual(expectedSecondRegionLocation, secondRegionLocation);
+ Assert.AreEqual("Two", SecondRegion.Name);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasEndRegionButNoStartRegion_NoRegionsAddedToCompilationUnit()
+ {
+ string code =
+ "var a = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ Assert.AreEqual(0, compilationUnit.FoldingRegions.Count);
+ }
+
+ [Test]
+ public void Parse_JavaScriptCodeHasTwoNestedRegions_RegionsHaveCorrectLocationsAndNames()
+ {
+ string code =
+ "//#region One\r\n" +
+ "var a = 1;\r\n" +
+ "//#region Two\r\n" +
+ "var b = 1;\r\n" +
+ "//#endregion\r\n" +
+ "var c = 1;\r\n" +
+ "//#endregion\r\n";
+
+ ParseJavaScript(code);
+
+ DomRegion firstRegionLocation = FirstRegion.Region;
+
+ int beginLine = 3;
+ int endLine = 5;
+ int beginColumn = 1;
+ int endColumn = 13;
+
+ var expectedFirstRegionLocation = new DomRegion(beginLine, beginColumn, endLine, endColumn);
+
+ DomRegion secondRegionLocation = SecondRegion.Region;
+
+ beginLine = 1;
+ endLine = 7;
+ beginColumn = 1;
+ endColumn = 13;
+
+ var expectedSecondRegionLocation = new DomRegion(beginLine, beginColumn, endLine, endColumn);
+
+ Assert.AreEqual(expectedFirstRegionLocation, firstRegionLocation);
+ Assert.AreEqual("Two", FirstRegion.Name);
+ Assert.AreEqual(expectedSecondRegionLocation, secondRegionLocation);
+ Assert.AreEqual("One", SecondRegion.Name);
+ }
}
}