Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dannycoates committed Apr 21, 2010
0 parents commit 31f9c99
Show file tree
Hide file tree
Showing 12 changed files with 467 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*/bin/
*/obj/
*.csproj.user
*.suo
20 changes: 20 additions & 0 deletions GherkinEditor.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GherkinEditor", "GherkinEditor\GherkinEditor.csproj", "{143F9E01-3B98-4F8B-B170-1A14B2742111}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{143F9E01-3B98-4F8B-B170-1A14B2742111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{143F9E01-3B98-4F8B-B170-1A14B2742111}.Debug|Any CPU.Build.0 = Debug|Any CPU
{143F9E01-3B98-4F8B-B170-1A14B2742111}.Release|Any CPU.ActiveCfg = Release|Any CPU
{143F9E01-3B98-4F8B-B170-1A14B2742111}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
34 changes: 34 additions & 0 deletions GherkinEditor/GherkinClassificationDefinitions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Text.Classification;
using System.Windows.Media;

namespace GherkinEditor
{
internal static class GherkinClassificationDefinitions
{
[Export]
[Name("feature")]
internal static ClassificationTypeDefinition featureClassificationDefinition = null;

[Export]
[Name("feature.scenario")]
[BaseDefinition("feature")]
internal static ClassificationTypeDefinition scenarioDefinition = null;

[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "feature.scenario")]
[Name("feature.scenario")]
internal sealed class ScenarioFormat : ClassificationFormatDefinition
{
public ScenarioFormat()
{
ForegroundColor = Colors.Blue;
}
}
}
}
45 changes: 45 additions & 0 deletions GherkinEditor/GherkinClassifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text;

namespace GherkinEditor
{
public class GherkinClassifier : IClassifier
{
private IClassificationTypeRegistryService _classificationTypeRegistry;

internal GherkinClassifier(IClassificationTypeRegistryService registry)
{
_classificationTypeRegistry = registry;
}

#pragma warning disable 67
public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
#pragma warning restore 67

public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
{
var snapshot = span.Snapshot;
var spans = new List<ClassificationSpan>();

if (snapshot.Length == 0)
{
return spans;
}
foreach (var line in snapshot.Lines)
{
var index = -1;
var text = line.GetText();
if ((index = text.IndexOf("Scenario:")) != -1)
{
var type = _classificationTypeRegistry.GetClassificationType("feature.scenario");
spans.Add(new ClassificationSpan(new SnapshotSpan(snapshot, line.Start.Position + index, 8), type));
}
}
return spans;
}
}
}
30 changes: 30 additions & 0 deletions GherkinEditor/GherkinClassifierProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Text;

namespace GherkinEditor
{
[Export(typeof(IClassifierProvider))]
[ContentType("feature")]
internal class GherkinClassifierProvider : IClassifierProvider
{
[Import]
internal IClassificationTypeRegistryService ClassificationRegistry = null;

private static GherkinClassifier gherkinClassifier;

public IClassifier GetClassifier(ITextBuffer textBuffer)
{
if (gherkinClassifier == null)
{
gherkinClassifier = new GherkinClassifier(ClassificationRegistry);
}
return gherkinClassifier;
}
}
}
22 changes: 22 additions & 0 deletions GherkinEditor/GherkinContentDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Utilities;

namespace GherkinEditor
{
internal static class GherkinContentDefinition
{
[Export]
[Name("feature")]
[BaseDefinition("text")]
internal static ContentTypeDefinition featureContentTypeDefinition = null;

[Export]
[FileExtension(".feature")]
[ContentType("feature")]
internal static FileExtensionToContentTypeDefinition featureFileExtensionDefinition = null;
}
}
74 changes: 74 additions & 0 deletions GherkinEditor/GherkinEditor.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>{143F9E01-3B98-4F8B-B170-1A14B2742111}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GherkinEditor</RootNamespace>
<AssemblyName>GherkinEditor</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<GeneratePkgDefFile>false</GeneratePkgDefFile>
<!--
<IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
<CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
-->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="GherkinClassificationDefinitions.cs" />
<Compile Include="GherkinClassifier.cs" />
<Compile Include="GherkinClassifierProvider.cs" />
<Compile Include="GherkinContentDefinition.cs" />
<Compile Include="GherkinOutlineTagger.cs" />
<Compile Include="GherkinOutlineTaggerProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Region.cs" />
</ItemGroup>
<ItemGroup>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Data, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Logic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.UI, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
147 changes: 147 additions & 0 deletions GherkinEditor/GherkinOutlineTagger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Text;

namespace GherkinEditor
{
internal sealed class GherkinOutlineTagger : ITagger<IOutliningRegionTag>
{
private readonly string[] _startHide = { "Scenario:", "Feature:", "Background:" };
private ITextBuffer _buffer;
private ITextSnapshot _snapshot;
private List<Region> _regions;

public GherkinOutlineTagger(ITextBuffer buffer)
{
_buffer = buffer;
_snapshot = buffer.CurrentSnapshot;
_regions = new List<Region>();
ReParse();
buffer.Changed += BufferChanged;
}

void BufferChanged(object sender, TextContentChangedEventArgs e)
{
if (e.After != _buffer.CurrentSnapshot)
{
return;
}
ReParse();
}

private void ReParse()
{
var newSnapshot = _buffer.CurrentSnapshot;
var newRegions = new List<Region>();

Region currentRegion = null;
var lastNonEmptyLine = 0;

foreach (var line in newSnapshot.Lines)
{
var text = line.GetText().Trim();

if (_startHide.Any(s => text.StartsWith(s)))
{
if (currentRegion != null)
{
currentRegion.EndLine = lastNonEmptyLine;
currentRegion.Hover = newSnapshot.GetText(AsSnapshotSpan(currentRegion, newSnapshot));
newRegions.Add(currentRegion);
}
currentRegion = new Region
{
Text = text,
StartLine = line.LineNumber
};
}
else if (line.GetLineBreakText().Length == 0)
{
if (currentRegion != null)
{
var end = (text.Length > 0) ? lastNonEmptyLine + 1 : lastNonEmptyLine;
currentRegion.EndLine = end;
currentRegion.Hover = newSnapshot.GetText(AsSnapshotSpan(currentRegion, newSnapshot));
newRegions.Add(currentRegion);
}
}
if (text.Length > 0)
{
lastNonEmptyLine = line.LineNumber;
}
}

var oldSpans = new List<Span>(_regions.Select(r => AsSnapshotSpan(r, _snapshot)
.TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive)
.Span));
var newSpans = new List<Span>(newRegions.Select(r => AsSnapshotSpan(r, newSnapshot).Span));
var oldSpanCol = new NormalizedSpanCollection(oldSpans);
var newSpanCol = new NormalizedSpanCollection(newSpans);
var removed = NormalizedSpanCollection.Difference(oldSpanCol, newSpanCol);
var added = NormalizedSpanCollection.Difference(newSpanCol, oldSpanCol);

var changeStart = int.MaxValue;
var changeEnd = -1;

if (removed.Count > 0)
{
changeStart = removed[0].Start;
changeEnd = removed[removed.Count - 1].End;
}

if (added.Count > 0)
{
changeStart = Math.Min(changeStart, added[0].Start);
changeEnd = Math.Max(changeEnd, added[added.Count - 1].End);
}

_snapshot = newSnapshot;
_regions = newRegions;

if (changeStart <= changeEnd)
{
if (TagsChanged != null)
{
TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(_snapshot, Span.FromBounds(changeStart, changeEnd))));
}
}
}

private static SnapshotSpan AsSnapshotSpan(Region region, ITextSnapshot snapshot)
{
var startLine = snapshot.GetLineFromLineNumber(region.StartLine);
var endLine = snapshot.GetLineFromLineNumber(region.EndLine);
return new SnapshotSpan(startLine.Start, endLine.End);
}

public IEnumerable<ITagSpan<IOutliningRegionTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (spans.Count == 0)
{
yield break;
}
var currentRegions = _regions;
var currentSnapshot = _snapshot;
var entire = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End)
.TranslateTo(currentSnapshot, SpanTrackingMode.EdgeExclusive);
var startLineNo = entire.Start.GetContainingLine().LineNumber;
var endLineNo = entire.End.GetContainingLine().LineNumber;
foreach (var region in currentRegions)
{
if (region.StartLine <= endLineNo && region.EndLine >= startLineNo)
{
var startLine = currentSnapshot.GetLineFromLineNumber(region.StartLine);
var endLine = currentSnapshot.GetLineFromLineNumber(region.EndLine);
yield return new TagSpan<IOutliningRegionTag>(
new SnapshotSpan(startLine.Start, endLine.End),
new OutliningRegionTag(false, false, region.Text, region.Hover));
}
}
}

public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
}
}
Loading

0 comments on commit 31f9c99

Please sign in to comment.