Skip to content

Commit

Permalink
Merge pull request #251 from OmniSharp/feature-highlighting
Browse files Browse the repository at this point in the history
Feature highlighting
  • Loading branch information
nosami committed Jul 3, 2015
2 parents 722f083 + 53b16a9 commit 5cc0723
Show file tree
Hide file tree
Showing 7 changed files with 525 additions and 1 deletion.
108 changes: 108 additions & 0 deletions src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Models;

namespace OmniSharp
{
public partial class OmnisharpController
{
[HttpPost("highlight")]
public async Task<HighlightResponse> Highlight(HighlightRequest request)
{
var documents = _workspace.GetDocuments(request.FileName);
if (request.ProjectNames != null && request.ProjectNames.Length > 0)
{
documents = documents.Where(d => request.ProjectNames.Contains(d.Project.Name, StringComparer.Ordinal));
}

if (request.Classifications == null || request.Classifications.Length > 0)
{
request.Classifications = AllClassifications;
}

if (request.ExcludeClassifications != null && request.ExcludeClassifications.Length > 0)
{
request.Classifications = request.Classifications.Except(request.ExcludeClassifications).ToArray();
}

var results = new List<ClassifiedResult>();

foreach (var document in documents)
{
var project = document.Project.Name;
var text = await document.GetTextAsync();
var spans = new List<ClassifiedSpan>();

if (request.Lines == null || request.Lines.Length == 0)
{
foreach (var span in await Classifier.GetClassifiedSpansAsync(document, new TextSpan(0, text.Length)))
{
spans.Add(span);
}
}
else
{
foreach (var line in request.Lines)
{
foreach (var span in await Classifier.GetClassifiedSpansAsync(document, text.Lines[line - 1].Span))
{
spans.Add(span);
}
}
}

results.AddRange(FilterSpans(request.Classifications, spans)
.Select(span => new ClassifiedResult()
{
Span = span,
Lines = text.Lines,
Project = project
}));
}

return new HighlightResponse()
{
Highlights = results
.GroupBy(result => result.Span.TextSpan.ToString())
.Select(grouping => HighlightSpan.FromClassifiedSpan(grouping.First().Span, grouping.First().Lines, grouping.Select(z => z.Project)))
.ToArray()
};
}

class ClassifiedResult
{
public ClassifiedSpan Span { get; set; }
public TextLineCollection Lines { get; set; }
public string Project { get; set; }
}

private HighlightClassification[] AllClassifications = Enum.GetValues(typeof(HighlightClassification)).Cast<HighlightClassification>().ToArray();

private IEnumerable<ClassifiedSpan> FilterSpans(HighlightClassification[] classifications, IEnumerable<ClassifiedSpan> spans)
{
foreach (var classification in AllClassifications.Except(classifications))
{
if (classification == HighlightClassification.Name)
spans = spans.Where(x => !x.ClassificationType.EndsWith(" name"));
else if (classification == HighlightClassification.Comment)
spans = spans.Where(x => x.ClassificationType != "comment" && !x.ClassificationType.StartsWith("xml doc comment "));
else if (classification == HighlightClassification.String)
spans = spans.Where(x => x.ClassificationType != "string" && !x.ClassificationType.StartsWith("string "));
else if (classification == HighlightClassification.PreprocessorKeyword)
spans = spans.Where(x => x.ClassificationType != "preprocessor keyword");
else if (classification == HighlightClassification.ExcludedCode)
spans = spans.Where(x => x.ClassificationType != "excluded code");
else
spans = spans.Where(x => x.ClassificationType != Enum.GetName(typeof(HighlightClassification), classification).ToLower());
}

return spans;
}
}
}
94 changes: 94 additions & 0 deletions src/OmniSharp/Models/v1/Highlight.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Text;

namespace OmniSharp.Models
{
public class HighlightSpan : IComparable<HighlightSpan>
{
public int StartLine { get; set; }
public int StartColumn { get; set; }
public int EndLine { get; set; }
public int EndColumn { get; set; }
public string Kind { get; set; }
public IEnumerable<string> Projects { get; set; }

internal static HighlightSpan FromClassifiedSpan(ClassifiedSpan span, TextLineCollection lines, IEnumerable<string> projects)
{
var linePos = lines.GetLinePositionSpan(span.TextSpan);

return new HighlightSpan
{
StartLine = linePos.Start.Line + 1,
EndLine = linePos.End.Line + 1,
StartColumn = linePos.Start.Character + 1,
EndColumn = linePos.End.Character + 1,
Kind = span.ClassificationType,
Projects = projects
};
}

public int CompareTo(HighlightSpan other)
{
if (other.StartLine < StartLine)
{
return 1;
}
else if (other.StartLine > StartLine)
{
return -1;
}
// same start line
else if (other.StartColumn < StartColumn)
{
return 1;
}
else if (other.StartColumn > StartColumn)
{
return -1;
}
// same start line and start column
else if (other.EndLine < EndLine)
{
return 1;
}
else if (other.EndLine > EndLine)
{
return -1;
}
// same start line, start column, and end line
else if (other.EndColumn < EndColumn)
{
return 1;
}
else if (other.EndColumn > EndColumn)
{
return -1;
}
// same, same
else
{
return 0;
}
}

public override bool Equals(object other)
{
var node = other as FileMemberElement;
return node != null
&& node.Location.Line == StartLine
&& node.Location.Column == StartColumn
&& node.Location.EndLine == EndLine
&& node.Location.EndColumn == EndColumn;
}

public override int GetHashCode()
{
return 13 * StartLine +
17 * StartColumn +
23 * EndLine +
31 * EndColumn;
}
}
}
21 changes: 21 additions & 0 deletions src/OmniSharp/Models/v1/HighlightClassification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Text;

namespace OmniSharp.Models
{
public enum HighlightClassification
{
Name = 1,
Comment = 2,
String = 3,
Operator = 4,
Punctuation = 5,
Keyword = 6,
Number = 7,
Identifier = 8,
PreprocessorKeyword = 9,
ExcludedCode = 10
}
}
25 changes: 25 additions & 0 deletions src/OmniSharp/Models/v1/HighlightRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace OmniSharp.Models
{
public class HighlightRequest : Request
{
/// <summary>
/// Specifies which lines to highlight.
/// If none are given, highlight the entire
/// file.
/// </summary>
public int[] Lines { get; set; }
/// <summary>
/// Specifies which projects to highlight for.
// If none are given, highlight for all the projects.
/// </summary>
public string[] ProjectNames { get; set; }
/// <summary>
/// Request specific classifications, if none are requested you will get them all.
/// </summary>
public HighlightClassification[] Classifications { get; set; }
/// <summary>
/// Exclude specific classifications
/// </summary>
public HighlightClassification[] ExcludeClassifications { get; set; }
}
}
7 changes: 7 additions & 0 deletions src/OmniSharp/Models/v1/HighlightResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace OmniSharp.Models
{
public class HighlightResponse
{
public HighlightSpan[] Highlights { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/OmniSharp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public Startup()
}

public IConfiguration Configuration { get; private set; }

public OmnisharpWorkspace Workspace { get; set; }

public void ConfigureServices(IServiceCollection services)
Expand Down
Loading

0 comments on commit 5cc0723

Please sign in to comment.