From f21fe79ad022b35912b6b18cab2185bc5d9392a0 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Wed, 29 Apr 2015 10:39:52 +0200 Subject: [PATCH 01/22] Ignore project.lock.json files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0d12a6283a..7810000c40 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,6 @@ nuget.exe *.ipch *.sln.ide debugSettings.json -buildlog /.vs +buildlog *.lock.json From f1c7e74bbef5b79d7b952c5a40cb66521e25825c Mon Sep 17 00:00:00 2001 From: Alxandr Date: Wed, 29 Apr 2015 10:40:13 +0200 Subject: [PATCH 02/22] Syntax highlighting Completely untested --- .../OmnisharpController.Highlighting.cs | 239 ++++++++++++++++++ src/OmniSharp/Models/HighlightRequest.cs | 12 + src/OmniSharp/Models/HighlightResponse.cs | 10 + 3 files changed, 261 insertions(+) create mode 100644 src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs create mode 100644 src/OmniSharp/Models/HighlightRequest.cs create mode 100644 src/OmniSharp/Models/HighlightResponse.cs diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs new file mode 100644 index 0000000000..65f8069c92 --- /dev/null +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -0,0 +1,239 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Recommendations; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using OmniSharp.Documentation; +using OmniSharp.Extensions; +using OmniSharp.Models; + +namespace OmniSharp +{ + public partial class OmnisharpController + { + [HttpPost("highlight")] + public async Task> Highlight(HighlightRequest request) + { + var document = _workspace.GetDocument(request.FileName); + var model = await document.GetSemanticModelAsync(); + var tree = await document.GetSyntaxTreeAsync(); + var root = await tree.GetRootAsync(); + + var nodes = new List(); + if (request.Lines.Length == 0) + { + nodes.Add(root); + } + else + { + var text = await tree.GetTextAsync(); + foreach (var line in request.Lines) + { + var lineSpan = text.Lines[line].Span; + var node = root; + var start = node.ChildThatContainsPosition(lineSpan.Start); + var end = node.ChildThatContainsPosition(lineSpan.End); + while (start.IsNode && start == end) + { + node = start.AsNode(); + } + + nodes.Add(node); + } + } + + var walker = new HighlightSyntaxWalker(model); + foreach (var node in nodes) + { + walker.Visit(node); + } + + return walker.Regions; + } + + class HighlightSyntaxWalker : CSharpSyntaxWalker + { + readonly SemanticModel _model; + readonly List _regions; + + // TODO: Fix overlap + public IImmutableList Regions + { + get + { + return _regions + .GroupBy(r => new { r.Line, r.Start, r.End }) + .Select(g => new HighlightResponse + { + Line = g.Key.Line, + Start = g.Key.Start, + End = g.Key.End, + Kind = string.Join(" ", g.Select(r => r.Kind).Distinct()) + }) + .ToImmutableList(); + } + } + + public HighlightSyntaxWalker(SemanticModel model) + : base(SyntaxWalkerDepth.Trivia) + { + _model = model; + } + + void Mark(SyntaxToken token, string type = null) + { + var location = token.GetLocation().GetLineSpan(); + var startLine = location.StartLinePosition.Line; + var endLine = location.EndLinePosition.Line; + + for (var i = startLine; i <= endLine; i++) + { + var start = i == startLine ? location.StartLinePosition.Character : 0; + var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; + + _regions.Add(new HighlightResponse + { + Line = i, + Start = start, + End = end, + Kind = type + }); + } + } + + void Mark(SyntaxTrivia trivia, string type = null) + { + var location = trivia.GetLocation().GetLineSpan(); + var startLine = location.StartLinePosition.Line; + var endLine = location.EndLinePosition.Line; + + for (var i = startLine; i <= endLine; i++) + { + var start = i == startLine ? location.StartLinePosition.Character : 0; + var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; + + _regions.Add(new HighlightResponse + { + Line = i, + Start = start, + End = end, + Kind = type + }); + } + } + + ISymbol GetTokenSymbol(SyntaxToken token) + { + var symbol = _model.GetDeclaredSymbol(token.Parent); + if (symbol == null) + { + // The token isnt part of a declaration node, so try to get symbol info. + symbol = _model.GetSymbolInfo(token.Parent).Symbol; + if (symbol == null) + { + // we couldnt find symbol information for the node, so we will look at all symbols in scope by name. + var namedSymbols = _model.LookupSymbols(token.SpanStart, null, token.ToString(), true); + if (namedSymbols.Length == 1) + { + symbol = namedSymbols[0]; + } + } + } + + return symbol; + } + + void MarkIdentifier(SyntaxToken token) + { + var symbol = GetTokenSymbol(token); + if (symbol == null) + { + Mark(token, "identifier"); + } + else + { + VisitSymbol(token, symbol); + } + } + + void VisitSymbol(SyntaxToken token, ISymbol symbol) + { + var parts = symbol.ToDisplayParts(new SymbolDisplayFormat()); + var part = parts.SingleOrDefault(p => p.Symbol == symbol); + if (part.Symbol != null) + { + Mark(token, part.Kind.ToString().ToLowerInvariant()); + } + } + + public override void VisitToken(SyntaxToken token) + { + if (token.IsKeyword() || token.IsContextualKeyword()) + { + Mark(token, "keyword"); + var symbol = GetTokenSymbol(token); + if (symbol != null) + { + VisitSymbol(token, symbol); + } + } + + var kind = token.Kind(); + switch (kind) + { + case SyntaxKind.IdentifierToken: + MarkIdentifier(token); break; + + case SyntaxKind.StringLiteralToken: + Mark(token, "string"); break; + + case SyntaxKind.NumericLiteralToken: + Mark(token, "number"); break; + + case SyntaxKind.CharacterLiteralToken: + Mark(token, "char"); break; + } + + base.VisitToken(token); + } + + public override void VisitTrivia(SyntaxTrivia trivia) + { + if (trivia.HasStructure) + Visit(trivia.GetStructure()); + + switch (trivia.Kind()) + { + case SyntaxKind.SingleLineDocumentationCommentTrivia: + case SyntaxKind.MultiLineDocumentationCommentTrivia: + case SyntaxKind.DocumentationCommentExteriorTrivia: + Mark(trivia, "doc"); + + goto case SyntaxKind.SingleLineCommentTrivia; + + case SyntaxKind.MultiLineCommentTrivia: + case SyntaxKind.SingleLineCommentTrivia: + Mark(trivia, "comment"); break; + + case SyntaxKind.EndOfLineTrivia: + case SyntaxKind.WhitespaceTrivia: + Mark(trivia, "whitespace"); break; + + case SyntaxKind.RegionDirectiveTrivia: + case SyntaxKind.EndRegionDirectiveTrivia: + Mark(trivia, "region"); break; + + case SyntaxKind.DisabledTextTrivia: + Mark(trivia, "disabled-text"); break; + } + + base.VisitTrivia(trivia); + } + } + } +} diff --git a/src/OmniSharp/Models/HighlightRequest.cs b/src/OmniSharp/Models/HighlightRequest.cs new file mode 100644 index 0000000000..04dd47354d --- /dev/null +++ b/src/OmniSharp/Models/HighlightRequest.cs @@ -0,0 +1,12 @@ +namespace OmniSharp.Models +{ + public class HighlightRequest : Request + { + /// + /// Spesifies which lines to highlight. + /// If none are given, highlight the entire + /// file. + /// + public int[] Lines { get; set; } + } +} diff --git a/src/OmniSharp/Models/HighlightResponse.cs b/src/OmniSharp/Models/HighlightResponse.cs new file mode 100644 index 0000000000..fe4ef8949f --- /dev/null +++ b/src/OmniSharp/Models/HighlightResponse.cs @@ -0,0 +1,10 @@ +namespace OmniSharp.Models +{ + public class HighlightResponse + { + public int Line { get; set; } + public int Start { get; set; } + public int End { get; set; } + public string Kind { get; set; } + } +} From d5f002b9f14573feba5cc9b22c6c0e098c749cb0 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Wed, 29 Apr 2015 10:54:56 +0200 Subject: [PATCH 03/22] Sort usings --- .../Api/Highlighting/OmnisharpController.Highlighting.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs index 65f8069c92..cf11ec4621 100644 --- a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -4,10 +4,10 @@ using System.Threading.Tasks; using Microsoft.AspNet.Mvc; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Recommendations; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Recommendations; +using Microsoft.CodeAnalysis.Text; using OmniSharp.Documentation; using OmniSharp.Extensions; using OmniSharp.Models; From ebe7d7d2f9ff78862e1fede467d6b6ddf70ae2b4 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Wed, 29 Apr 2015 11:31:44 +0200 Subject: [PATCH 04/22] Small fixes and refactoring --- .../OmnisharpController.Highlighting.cs | 197 ++---------------- src/OmniSharp/Models/HighlightRequest.cs | 3 + .../Highlighting/HighlightSyntaxWalker.cs | 193 +++++++++++++++++ 3 files changed, 209 insertions(+), 184 deletions(-) create mode 100644 src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs index cf11ec4621..35783b19c2 100644 --- a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -25,7 +25,7 @@ public async Task> Highlight(HighlightRequest req var root = await tree.GetRootAsync(); var nodes = new List(); - if (request.Lines.Length == 0) + if (request.Lines == null || request.Lines.Length == 0) { nodes.Add(root); } @@ -36,12 +36,15 @@ public async Task> Highlight(HighlightRequest req { var lineSpan = text.Lines[line].Span; var node = root; - var start = node.ChildThatContainsPosition(lineSpan.Start); - var end = node.ChildThatContainsPosition(lineSpan.End); - while (start.IsNode && start == end) + var next = node; + SyntaxNodeOrToken start, end; + do { - node = start.AsNode(); - } + node = next; + start = node.ChildThatContainsPosition(lineSpan.Start); + end = node.ChildThatContainsPosition(lineSpan.End); + next = start.AsNode(); + } while (start.IsNode && start == end); nodes.Add(node); } @@ -53,187 +56,13 @@ public async Task> Highlight(HighlightRequest req walker.Visit(node); } - return walker.Regions; - } - - class HighlightSyntaxWalker : CSharpSyntaxWalker - { - readonly SemanticModel _model; - readonly List _regions; - - // TODO: Fix overlap - public IImmutableList Regions - { - get - { - return _regions - .GroupBy(r => new { r.Line, r.Start, r.End }) - .Select(g => new HighlightResponse - { - Line = g.Key.Line, - Start = g.Key.Start, - End = g.Key.End, - Kind = string.Join(" ", g.Select(r => r.Kind).Distinct()) - }) - .ToImmutableList(); - } - } - - public HighlightSyntaxWalker(SemanticModel model) - : base(SyntaxWalkerDepth.Trivia) - { - _model = model; - } - - void Mark(SyntaxToken token, string type = null) - { - var location = token.GetLocation().GetLineSpan(); - var startLine = location.StartLinePosition.Line; - var endLine = location.EndLinePosition.Line; - - for (var i = startLine; i <= endLine; i++) - { - var start = i == startLine ? location.StartLinePosition.Character : 0; - var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; - - _regions.Add(new HighlightResponse - { - Line = i, - Start = start, - End = end, - Kind = type - }); - } - } - - void Mark(SyntaxTrivia trivia, string type = null) - { - var location = trivia.GetLocation().GetLineSpan(); - var startLine = location.StartLinePosition.Line; - var endLine = location.EndLinePosition.Line; - - for (var i = startLine; i <= endLine; i++) - { - var start = i == startLine ? location.StartLinePosition.Character : 0; - var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; - - _regions.Add(new HighlightResponse - { - Line = i, - Start = start, - End = end, - Kind = type - }); - } - } - - ISymbol GetTokenSymbol(SyntaxToken token) - { - var symbol = _model.GetDeclaredSymbol(token.Parent); - if (symbol == null) - { - // The token isnt part of a declaration node, so try to get symbol info. - symbol = _model.GetSymbolInfo(token.Parent).Symbol; - if (symbol == null) - { - // we couldnt find symbol information for the node, so we will look at all symbols in scope by name. - var namedSymbols = _model.LookupSymbols(token.SpanStart, null, token.ToString(), true); - if (namedSymbols.Length == 1) - { - symbol = namedSymbols[0]; - } - } - } - - return symbol; - } - - void MarkIdentifier(SyntaxToken token) + var regions = walker.Regions; + if (request.Lines != null && request.Lines.Length != 0) { - var symbol = GetTokenSymbol(token); - if (symbol == null) - { - Mark(token, "identifier"); - } - else - { - VisitSymbol(token, symbol); - } - } - - void VisitSymbol(SyntaxToken token, ISymbol symbol) - { - var parts = symbol.ToDisplayParts(new SymbolDisplayFormat()); - var part = parts.SingleOrDefault(p => p.Symbol == symbol); - if (part.Symbol != null) - { - Mark(token, part.Kind.ToString().ToLowerInvariant()); - } - } - - public override void VisitToken(SyntaxToken token) - { - if (token.IsKeyword() || token.IsContextualKeyword()) - { - Mark(token, "keyword"); - var symbol = GetTokenSymbol(token); - if (symbol != null) - { - VisitSymbol(token, symbol); - } - } - - var kind = token.Kind(); - switch (kind) - { - case SyntaxKind.IdentifierToken: - MarkIdentifier(token); break; - - case SyntaxKind.StringLiteralToken: - Mark(token, "string"); break; - - case SyntaxKind.NumericLiteralToken: - Mark(token, "number"); break; - - case SyntaxKind.CharacterLiteralToken: - Mark(token, "char"); break; - } - - base.VisitToken(token); + return regions.Where(r => request.Lines.Contains(r.Line)); } - public override void VisitTrivia(SyntaxTrivia trivia) - { - if (trivia.HasStructure) - Visit(trivia.GetStructure()); - - switch (trivia.Kind()) - { - case SyntaxKind.SingleLineDocumentationCommentTrivia: - case SyntaxKind.MultiLineDocumentationCommentTrivia: - case SyntaxKind.DocumentationCommentExteriorTrivia: - Mark(trivia, "doc"); - - goto case SyntaxKind.SingleLineCommentTrivia; - - case SyntaxKind.MultiLineCommentTrivia: - case SyntaxKind.SingleLineCommentTrivia: - Mark(trivia, "comment"); break; - - case SyntaxKind.EndOfLineTrivia: - case SyntaxKind.WhitespaceTrivia: - Mark(trivia, "whitespace"); break; - - case SyntaxKind.RegionDirectiveTrivia: - case SyntaxKind.EndRegionDirectiveTrivia: - Mark(trivia, "region"); break; - - case SyntaxKind.DisabledTextTrivia: - Mark(trivia, "disabled-text"); break; - } - - base.VisitTrivia(trivia); - } + return regions; } } } diff --git a/src/OmniSharp/Models/HighlightRequest.cs b/src/OmniSharp/Models/HighlightRequest.cs index 04dd47354d..fc5005f460 100644 --- a/src/OmniSharp/Models/HighlightRequest.cs +++ b/src/OmniSharp/Models/HighlightRequest.cs @@ -7,6 +7,9 @@ public class HighlightRequest : Request /// If none are given, highlight the entire /// file. /// + /// + /// This is 0 indexed. + /// public int[] Lines { get; set; } } } diff --git a/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs b/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs new file mode 100644 index 0000000000..3f9a1da3be --- /dev/null +++ b/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs @@ -0,0 +1,193 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Recommendations; +using Microsoft.CodeAnalysis.Text; +using OmniSharp.Models; + +namespace OmniSharp +{ + public class HighlightSyntaxWalker : CSharpSyntaxWalker + { + readonly SemanticModel _model; + readonly List _regions; + + // TODO: Fix overlap + public IImmutableList Regions + { + get + { + return _regions + .Where(r => r.End - r.Start > 0) + .GroupBy(r => new { r.Line, r.Start, r.End }) + .Select(g => new HighlightResponse + { + Line = g.Key.Line, + Start = g.Key.Start, + End = g.Key.End, + Kind = string.Join(" ", g.Select(r => r.Kind).Distinct()) + }) + .OrderBy(r => r.Line) + .ThenBy(r => r.Start) + .ToImmutableList(); + } + } + + public HighlightSyntaxWalker(SemanticModel model) + : base(SyntaxWalkerDepth.Trivia) + { + _model = model; + _regions = new List(); + } + + void Mark(SyntaxToken token, string type = null) + { + var location = token.GetLocation().GetLineSpan(); + var startLine = location.StartLinePosition.Line; + var endLine = location.EndLinePosition.Line; + + for (var i = startLine; i <= endLine; i++) + { + var start = i == startLine ? location.StartLinePosition.Character : 0; + var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; + + _regions.Add(new HighlightResponse + { + Line = i, + Start = start, + End = end, + Kind = type + }); + } + } + + void Mark(SyntaxTrivia trivia, string type = null) + { + var location = trivia.GetLocation().GetLineSpan(); + var startLine = location.StartLinePosition.Line; + var endLine = location.EndLinePosition.Line; + + for (var i = startLine; i <= endLine; i++) + { + var start = i == startLine ? location.StartLinePosition.Character : 0; + var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; + + _regions.Add(new HighlightResponse + { + Line = i, + Start = start, + End = end, + Kind = type + }); + } + } + + ISymbol GetTokenSymbol(SyntaxToken token) + { + var symbol = _model.GetDeclaredSymbol(token.Parent); + if (symbol == null) + { + // The token isnt part of a declaration node, so try to get symbol info. + symbol = _model.GetSymbolInfo(token.Parent).Symbol; + if (symbol == null) + { + // we couldnt find symbol information for the node, so we will look at all symbols in scope by name. + var namedSymbols = _model.LookupSymbols(token.SpanStart, null, token.ToString(), true); + if (namedSymbols.Length == 1) + { + symbol = namedSymbols[0]; + } + } + } + + return symbol; + } + + void MarkIdentifier(SyntaxToken token) + { + var symbol = GetTokenSymbol(token); + if (symbol == null) + { + Mark(token, "identifier"); + } + else + { + VisitSymbol(token, symbol); + } + } + + void VisitSymbol(SyntaxToken token, ISymbol symbol) + { + var parts = symbol.ToDisplayParts(new SymbolDisplayFormat()); + var part = parts.SingleOrDefault(p => p.Symbol == symbol); + if (part.Symbol != null) + { + Mark(token, part.Kind.ToString().ToLowerInvariant()); + } + } + + public override void VisitToken(SyntaxToken token) + { + if (token.IsKeyword() || token.IsContextualKeyword()) + { + Mark(token, "keyword"); + var symbol = GetTokenSymbol(token); + if (symbol != null) + { + VisitSymbol(token, symbol); + } + } + + var kind = token.Kind(); + switch (kind) + { + case SyntaxKind.IdentifierToken: + MarkIdentifier(token); break; + + case SyntaxKind.StringLiteralToken: + Mark(token, "string"); break; + + case SyntaxKind.NumericLiteralToken: + Mark(token, "number"); break; + + case SyntaxKind.CharacterLiteralToken: + Mark(token, "char"); break; + } + + base.VisitToken(token); + } + + public override void VisitTrivia(SyntaxTrivia trivia) + { + if (trivia.HasStructure) + Visit(trivia.GetStructure()); + + switch (trivia.Kind()) + { + case SyntaxKind.SingleLineDocumentationCommentTrivia: + case SyntaxKind.MultiLineDocumentationCommentTrivia: + case SyntaxKind.DocumentationCommentExteriorTrivia: + Mark(trivia, "doc"); + + goto case SyntaxKind.SingleLineCommentTrivia; + + case SyntaxKind.MultiLineCommentTrivia: + case SyntaxKind.SingleLineCommentTrivia: + Mark(trivia, "comment"); break; + + case SyntaxKind.RegionDirectiveTrivia: + case SyntaxKind.EndRegionDirectiveTrivia: + Mark(trivia, "region"); break; + + case SyntaxKind.DisabledTextTrivia: + Mark(trivia, "disabled-text"); break; + } + + base.VisitTrivia(trivia); + } + } +} From 255f02020481f3321094bdffba31e0788ea181d8 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Wed, 29 Apr 2015 12:00:34 +0200 Subject: [PATCH 05/22] Unittests --- tests/OmniSharp.Tests/HighlightFacts.cs | 89 +++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/OmniSharp.Tests/HighlightFacts.cs diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs new file mode 100644 index 0000000000..44f3a36544 --- /dev/null +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using OmniSharp.Models; +using Xunit; + +namespace OmniSharp.Tests +{ + public class HighlightFacts + { + [Fact] + public async Task HighlightSingleLine() + { + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", "namespace N1\n{\nclass C1 { int n = true; }\n}" } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = (await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 2 } })).ToList(); + + Assert.Equal(5, regions.Count); + Assert.Equal(2, regions[0].Line); + Assert.Equal(2, regions[1].Line); + Assert.Equal(2, regions[2].Line); + Assert.Equal(2, regions[3].Line); + Assert.Equal(2, regions[4].Line); + + Assert.Equal("keyword classname", regions[0].Kind); + Assert.Equal(0, regions[0].Start); + Assert.Equal(5, regions[0].End); + + Assert.Equal("classname", regions[1].Kind); + Assert.Equal(6, regions[1].Start); + Assert.Equal(8, regions[1].End); + + Assert.Equal("keyword structname", regions[2].Kind); + Assert.Equal(11, regions[2].Start); + Assert.Equal(14, regions[2].End); + + Assert.Equal("fieldname", regions[3].Kind); + Assert.Equal(15, regions[3].Start); + Assert.Equal(16, regions[3].End); + + Assert.Equal("keyword", regions[4].Kind); + Assert.Equal(19, regions[4].Start); + Assert.Equal(23, regions[4].End); + } + + [Fact] + public async Task HighlightEntireFile() + { + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", "class C1 { int n = true; }" } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = (await controller.Highlight(new HighlightRequest() { FileName = "a.cs" })).ToList(); + + Assert.Equal(5, regions.Count); + Assert.Equal(0, regions[0].Line); + Assert.Equal(0, regions[1].Line); + Assert.Equal(0, regions[2].Line); + Assert.Equal(0, regions[3].Line); + Assert.Equal(0, regions[4].Line); + + Assert.Equal("keyword classname", regions[0].Kind); + Assert.Equal(0, regions[0].Start); + Assert.Equal(5, regions[0].End); + + Assert.Equal("classname", regions[1].Kind); + Assert.Equal(6, regions[1].Start); + Assert.Equal(8, regions[1].End); + + Assert.Equal("keyword structname", regions[2].Kind); + Assert.Equal(11, regions[2].Start); + Assert.Equal(14, regions[2].End); + + Assert.Equal("fieldname", regions[3].Kind); + Assert.Equal(15, regions[3].Start); + Assert.Equal(16, regions[3].End); + + Assert.Equal("keyword", regions[4].Kind); + Assert.Equal(19, regions[4].Start); + Assert.Equal(23, regions[4].End); + } + } +} From a1d8a8b8d774ab195a55ae6defbe3564e76e9bb2 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Wed, 29 Apr 2015 22:14:09 +0200 Subject: [PATCH 06/22] Fixed some fowler issues --- .../Api/Highlighting/OmnisharpController.Highlighting.cs | 5 +++-- src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs index 35783b19c2..d4c53a1d38 100644 --- a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -25,7 +25,8 @@ public async Task> Highlight(HighlightRequest req var root = await tree.GetRootAsync(); var nodes = new List(); - if (request.Lines == null || request.Lines.Length == 0) + var highlightFile = request.Lines == null || request.Lines.Length == 0; + if (highlightFile) { nodes.Add(root); } @@ -57,7 +58,7 @@ public async Task> Highlight(HighlightRequest req } var regions = walker.Regions; - if (request.Lines != null && request.Lines.Length != 0) + if (!highlightFile) { return regions.Where(r => request.Lines.Contains(r.Line)); } diff --git a/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs b/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs index 3f9a1da3be..d66f478347 100644 --- a/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs +++ b/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs @@ -13,8 +13,8 @@ namespace OmniSharp { public class HighlightSyntaxWalker : CSharpSyntaxWalker { - readonly SemanticModel _model; - readonly List _regions; + private readonly SemanticModel _model; + private readonly List _regions; // TODO: Fix overlap public IImmutableList Regions From 9f20d8213ec0b31507488d4220c30bfb76508a76 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Thu, 30 Apr 2015 21:40:02 +0200 Subject: [PATCH 07/22] Use built in Roslyn Classifier Wow, Roslyn is awesome :+1: --- .../OmnisharpController.Highlighting.cs | 51 +---- src/OmniSharp/Models/HighlightResponse.cs | 20 +- .../Highlighting/HighlightSyntaxWalker.cs | 193 ------------------ tests/OmniSharp.Tests/HighlightFacts.cs | 126 +++++++----- 4 files changed, 96 insertions(+), 294 deletions(-) delete mode 100644 src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs index d4c53a1d38..2045b8d74d 100644 --- a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -1,15 +1,9 @@ using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Recommendations; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Text; -using OmniSharp.Documentation; -using OmniSharp.Extensions; using OmniSharp.Models; namespace OmniSharp @@ -21,49 +15,22 @@ public async Task> Highlight(HighlightRequest req { var document = _workspace.GetDocument(request.FileName); var model = await document.GetSemanticModelAsync(); - var tree = await document.GetSyntaxTreeAsync(); - var root = await tree.GetRootAsync(); - - var nodes = new List(); - var highlightFile = request.Lines == null || request.Lines.Length == 0; - if (highlightFile) + var text = await document.GetTextAsync(); + + var results = new List(); + if (request.Lines == null || request.Lines.Length == 0) { - nodes.Add(root); + results.AddRange(Classifier.GetClassifiedSpans(model, new TextSpan(0, text.Length), _workspace)); } else { - var text = await tree.GetTextAsync(); foreach (var line in request.Lines) { - var lineSpan = text.Lines[line].Span; - var node = root; - var next = node; - SyntaxNodeOrToken start, end; - do - { - node = next; - start = node.ChildThatContainsPosition(lineSpan.Start); - end = node.ChildThatContainsPosition(lineSpan.End); - next = start.AsNode(); - } while (start.IsNode && start == end); - - nodes.Add(node); + results.AddRange(Classifier.GetClassifiedSpans(model, text.Lines[line].Span, _workspace)); } } - - var walker = new HighlightSyntaxWalker(model); - foreach (var node in nodes) - { - walker.Visit(node); - } - - var regions = walker.Regions; - if (!highlightFile) - { - return regions.Where(r => request.Lines.Contains(r.Line)); - } - - return regions; + + return results.Select(s => HighlightResponse.FromClassifiedSpan(s, text.Lines)); } } } diff --git a/src/OmniSharp/Models/HighlightResponse.cs b/src/OmniSharp/Models/HighlightResponse.cs index fe4ef8949f..20f7accf30 100644 --- a/src/OmniSharp/Models/HighlightResponse.cs +++ b/src/OmniSharp/Models/HighlightResponse.cs @@ -1,10 +1,24 @@ +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Text; + namespace OmniSharp.Models { public class HighlightResponse { - public int Line { get; set; } - public int Start { get; set; } - public int End { get; set; } + public LinePosition Start { get; set; } + public LinePosition End { get; set; } public string Kind { get; set; } + + internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLineCollection lines) + { + var linePos = lines.GetLinePositionSpan(span.TextSpan); + + return new HighlightResponse + { + Start = linePos.Start, + End = linePos.End, + Kind = span.ClassificationType + }; + } } } diff --git a/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs b/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs deleted file mode 100644 index d66f478347..0000000000 --- a/src/OmniSharp/Workers/Highlighting/HighlightSyntaxWalker.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Recommendations; -using Microsoft.CodeAnalysis.Text; -using OmniSharp.Models; - -namespace OmniSharp -{ - public class HighlightSyntaxWalker : CSharpSyntaxWalker - { - private readonly SemanticModel _model; - private readonly List _regions; - - // TODO: Fix overlap - public IImmutableList Regions - { - get - { - return _regions - .Where(r => r.End - r.Start > 0) - .GroupBy(r => new { r.Line, r.Start, r.End }) - .Select(g => new HighlightResponse - { - Line = g.Key.Line, - Start = g.Key.Start, - End = g.Key.End, - Kind = string.Join(" ", g.Select(r => r.Kind).Distinct()) - }) - .OrderBy(r => r.Line) - .ThenBy(r => r.Start) - .ToImmutableList(); - } - } - - public HighlightSyntaxWalker(SemanticModel model) - : base(SyntaxWalkerDepth.Trivia) - { - _model = model; - _regions = new List(); - } - - void Mark(SyntaxToken token, string type = null) - { - var location = token.GetLocation().GetLineSpan(); - var startLine = location.StartLinePosition.Line; - var endLine = location.EndLinePosition.Line; - - for (var i = startLine; i <= endLine; i++) - { - var start = i == startLine ? location.StartLinePosition.Character : 0; - var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; - - _regions.Add(new HighlightResponse - { - Line = i, - Start = start, - End = end, - Kind = type - }); - } - } - - void Mark(SyntaxTrivia trivia, string type = null) - { - var location = trivia.GetLocation().GetLineSpan(); - var startLine = location.StartLinePosition.Line; - var endLine = location.EndLinePosition.Line; - - for (var i = startLine; i <= endLine; i++) - { - var start = i == startLine ? location.StartLinePosition.Character : 0; - var end = i == endLine ? location.EndLinePosition.Character : int.MaxValue; - - _regions.Add(new HighlightResponse - { - Line = i, - Start = start, - End = end, - Kind = type - }); - } - } - - ISymbol GetTokenSymbol(SyntaxToken token) - { - var symbol = _model.GetDeclaredSymbol(token.Parent); - if (symbol == null) - { - // The token isnt part of a declaration node, so try to get symbol info. - symbol = _model.GetSymbolInfo(token.Parent).Symbol; - if (symbol == null) - { - // we couldnt find symbol information for the node, so we will look at all symbols in scope by name. - var namedSymbols = _model.LookupSymbols(token.SpanStart, null, token.ToString(), true); - if (namedSymbols.Length == 1) - { - symbol = namedSymbols[0]; - } - } - } - - return symbol; - } - - void MarkIdentifier(SyntaxToken token) - { - var symbol = GetTokenSymbol(token); - if (symbol == null) - { - Mark(token, "identifier"); - } - else - { - VisitSymbol(token, symbol); - } - } - - void VisitSymbol(SyntaxToken token, ISymbol symbol) - { - var parts = symbol.ToDisplayParts(new SymbolDisplayFormat()); - var part = parts.SingleOrDefault(p => p.Symbol == symbol); - if (part.Symbol != null) - { - Mark(token, part.Kind.ToString().ToLowerInvariant()); - } - } - - public override void VisitToken(SyntaxToken token) - { - if (token.IsKeyword() || token.IsContextualKeyword()) - { - Mark(token, "keyword"); - var symbol = GetTokenSymbol(token); - if (symbol != null) - { - VisitSymbol(token, symbol); - } - } - - var kind = token.Kind(); - switch (kind) - { - case SyntaxKind.IdentifierToken: - MarkIdentifier(token); break; - - case SyntaxKind.StringLiteralToken: - Mark(token, "string"); break; - - case SyntaxKind.NumericLiteralToken: - Mark(token, "number"); break; - - case SyntaxKind.CharacterLiteralToken: - Mark(token, "char"); break; - } - - base.VisitToken(token); - } - - public override void VisitTrivia(SyntaxTrivia trivia) - { - if (trivia.HasStructure) - Visit(trivia.GetStructure()); - - switch (trivia.Kind()) - { - case SyntaxKind.SingleLineDocumentationCommentTrivia: - case SyntaxKind.MultiLineDocumentationCommentTrivia: - case SyntaxKind.DocumentationCommentExteriorTrivia: - Mark(trivia, "doc"); - - goto case SyntaxKind.SingleLineCommentTrivia; - - case SyntaxKind.MultiLineCommentTrivia: - case SyntaxKind.SingleLineCommentTrivia: - Mark(trivia, "comment"); break; - - case SyntaxKind.RegionDirectiveTrivia: - case SyntaxKind.EndRegionDirectiveTrivia: - Mark(trivia, "region"); break; - - case SyntaxKind.DisabledTextTrivia: - Mark(trivia, "disabled-text"); break; - } - - base.VisitTrivia(trivia); - } - } -} diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index 44f3a36544..7e452e6ac1 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -17,34 +17,18 @@ public async Task HighlightSingleLine() }); var controller = new OmnisharpController(workspace, null); - var regions = (await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 2 } })).ToList(); - - Assert.Equal(5, regions.Count); - Assert.Equal(2, regions[0].Line); - Assert.Equal(2, regions[1].Line); - Assert.Equal(2, regions[2].Line); - Assert.Equal(2, regions[3].Line); - Assert.Equal(2, regions[4].Line); - - Assert.Equal("keyword classname", regions[0].Kind); - Assert.Equal(0, regions[0].Start); - Assert.Equal(5, regions[0].End); - - Assert.Equal("classname", regions[1].Kind); - Assert.Equal(6, regions[1].Start); - Assert.Equal(8, regions[1].End); - - Assert.Equal("keyword structname", regions[2].Kind); - Assert.Equal(11, regions[2].Start); - Assert.Equal(14, regions[2].End); - - Assert.Equal("fieldname", regions[3].Kind); - Assert.Equal(15, regions[3].Start); - Assert.Equal(16, regions[3].End); - - Assert.Equal("keyword", regions[4].Kind); - Assert.Equal(19, regions[4].Start); - Assert.Equal(23, regions[4].End); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 2 } }); + + ValidateRegions(regions, + new Region("keyword", 2, 0, 5), + new Region("class name", 2, 6, 8), + new Region("punctuation", 2, 9, 10), + new Region("keyword", 2, 11, 14), + new Region("identifier", 2, 15, 16), + new Region("operator", 2, 17, 18), + new Region("keyword", 2, 19, 23), + new Region("punctuation", 2, 23, 24), + new Region("punctuation", 2, 25, 26)); } [Fact] @@ -56,34 +40,64 @@ public async Task HighlightEntireFile() }); var controller = new OmnisharpController(workspace, null); - var regions = (await controller.Highlight(new HighlightRequest() { FileName = "a.cs" })).ToList(); - - Assert.Equal(5, regions.Count); - Assert.Equal(0, regions[0].Line); - Assert.Equal(0, regions[1].Line); - Assert.Equal(0, regions[2].Line); - Assert.Equal(0, regions[3].Line); - Assert.Equal(0, regions[4].Line); - - Assert.Equal("keyword classname", regions[0].Kind); - Assert.Equal(0, regions[0].Start); - Assert.Equal(5, regions[0].End); - - Assert.Equal("classname", regions[1].Kind); - Assert.Equal(6, regions[1].Start); - Assert.Equal(8, regions[1].End); - - Assert.Equal("keyword structname", regions[2].Kind); - Assert.Equal(11, regions[2].Start); - Assert.Equal(14, regions[2].End); - - Assert.Equal("fieldname", regions[3].Kind); - Assert.Equal(15, regions[3].Start); - Assert.Equal(16, regions[3].End); - - Assert.Equal("keyword", regions[4].Kind); - Assert.Equal(19, regions[4].Start); - Assert.Equal(23, regions[4].End); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); + + ValidateRegions(regions, + new Region("keyword", 0, 0, 5), + new Region("class name", 0, 6, 8), + new Region("punctuation", 0, 9, 10), + new Region("keyword", 0, 11, 14), + new Region("identifier", 0, 15, 16), + new Region("operator", 0, 17, 18), + new Region("keyword", 0, 19, 23), + new Region("punctuation", 0, 23, 24), + new Region("punctuation", 0, 25, 26)); + } + + private void ValidateRegions(IEnumerable regions, params Region[] expected) + { + var arr = regions.ToArray(); + Assert.Equal(expected.Length, arr.Length); + + for (var i = 0; i < arr.Length; i++) + { + var expect = expected[i]; + var result = arr[i]; + + Assert.Equal(expect.Kind, result.Kind); + + Assert.Equal(expect.StartLine, result.Start.Line); + Assert.Equal(expect.StartChar, result.Start.Character); + + Assert.Equal(expect.EndLine, result.End.Line); + Assert.Equal(expect.EndChar, result.End.Character); + } + } + + private class Region + { + public int StartLine { get; } + public int StartChar { get; } + public int EndLine { get; } + public int EndChar { get; } + public string Kind { get; } + + public Region(string kind, int line, int start, int end) + { + Kind = kind; + StartLine = EndLine = line; + StartChar = start; + EndChar = end; + } + + public Region(string kind, int startLine, int startChar, int endLine, int endChar) + { + Kind = kind; + StartLine = startLine; + EndLine = endLine; + StartChar = startChar; + EndChar = endChar; + } } } } From 720f8dc92a9b61ab28402be3e6b823a14a834cc5 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Thu, 30 Apr 2015 22:00:28 +0200 Subject: [PATCH 08/22] Test highlighting in string interpolation --- tests/OmniSharp.Tests/HighlightFacts.cs | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index 7e452e6ac1..934459743c 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -54,6 +54,33 @@ public async Task HighlightEntireFile() new Region("punctuation", 0, 25, 26)); } + [Fact] + public async Task HighlightStringInterpolation() + { + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", "class C1 { string s = $\"{5}\"; }" } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); + + ValidateRegions(regions, + new Region("keyword", 0, 0, 5), + new Region("class name", 0, 6, 8), + new Region("punctuation", 0, 9, 10), + new Region("keyword", 0, 11, 17), + new Region("identifier", 0, 18, 19), + new Region("operator", 0, 20, 21), + new Region("string", 0, 22, 24), + new Region("punctuation", 0, 24, 25), + new Region("number", 0, 25, 26), + new Region("punctuation", 0, 26, 27), + new Region("string", 0, 27, 28), + new Region("punctuation", 0, 28, 29), + new Region("punctuation", 0, 30, 31)); + } + private void ValidateRegions(IEnumerable regions, params Region[] expected) { var arr = regions.ToArray(); From 0805ca13610e2e441d77ea9593cfcee81d634400 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Fri, 1 May 2015 11:52:39 +0200 Subject: [PATCH 09/22] Fix english --- src/OmniSharp/Models/HighlightRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OmniSharp/Models/HighlightRequest.cs b/src/OmniSharp/Models/HighlightRequest.cs index fc5005f460..2f6802adaf 100644 --- a/src/OmniSharp/Models/HighlightRequest.cs +++ b/src/OmniSharp/Models/HighlightRequest.cs @@ -3,7 +3,7 @@ namespace OmniSharp.Models public class HighlightRequest : Request { /// - /// Spesifies which lines to highlight. + /// Specifies which lines to highlight. /// If none are given, highlight the entire /// file. /// From 68ffaded374b8bcd3621183b8a528ddbfee6621b Mon Sep 17 00:00:00 2001 From: Alxandr Date: Fri, 1 May 2015 12:49:03 +0200 Subject: [PATCH 10/22] Pretify tests --- tests/OmniSharp.Tests/HighlightFacts.cs | 166 ++++++++++++++---------- 1 file changed, 100 insertions(+), 66 deletions(-) diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index 934459743c..898b990222 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -11,119 +12,152 @@ public class HighlightFacts [Fact] public async Task HighlightSingleLine() { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { - { "a.cs", "namespace N1\n{\nclass C1 { int n = true; }\n}" } + { "a.cs", code } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 2 } }); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 3 } }); - ValidateRegions(regions, - new Region("keyword", 2, 0, 5), - new Region("class name", 2, 6, 8), - new Region("punctuation", 2, 9, 10), - new Region("keyword", 2, 11, 14), - new Region("identifier", 2, 15, 16), - new Region("operator", 2, 17, 18), - new Region("keyword", 2, 19, 23), - new Region("punctuation", 2, 23, 24), - new Region("punctuation", 2, 25, 26)); + AssertSyntax(regions, code, 3, + Token("class", "keyword"), + Token("C1", "class name"), + Token("{", "punctuation"), + Token("int", "keyword"), + Token("n", "identifier"), + Token("=", "operator"), + Token("true", "keyword"), + Token(";", "punctuation"), + Token("}", "punctuation")); } [Fact] public async Task HighlightEntireFile() { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { - { "a.cs", "class C1 { int n = true; }" } + { "a.cs", code } }); var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); - ValidateRegions(regions, - new Region("keyword", 0, 0, 5), - new Region("class name", 0, 6, 8), - new Region("punctuation", 0, 9, 10), - new Region("keyword", 0, 11, 14), - new Region("identifier", 0, 15, 16), - new Region("operator", 0, 17, 18), - new Region("keyword", 0, 19, 23), - new Region("punctuation", 0, 23, 24), - new Region("punctuation", 0, 25, 26)); + AssertSyntax(regions, code, 0, + Token("namespace", "keyword"), + Token("N1", "identifier"), + Token("{", "punctuation"), + Token("class", "keyword"), + Token("C1", "class name"), + Token("{", "punctuation"), + Token("int", "keyword"), + Token("n", "identifier"), + Token("=", "operator"), + Token("true", "keyword"), + Token(";", "punctuation"), + Token("}", "punctuation"), + Token("}", "punctuation")); } [Fact] public async Task HighlightStringInterpolation() { + var code = @" + class C1 + { + string s = $""{5}""; + } + "; + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { - { "a.cs", "class C1 { string s = $\"{5}\"; }" } + { "a.cs", code } }); var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); - ValidateRegions(regions, - new Region("keyword", 0, 0, 5), - new Region("class name", 0, 6, 8), - new Region("punctuation", 0, 9, 10), - new Region("keyword", 0, 11, 17), - new Region("identifier", 0, 18, 19), - new Region("operator", 0, 20, 21), - new Region("string", 0, 22, 24), - new Region("punctuation", 0, 24, 25), - new Region("number", 0, 25, 26), - new Region("punctuation", 0, 26, 27), - new Region("string", 0, 27, 28), - new Region("punctuation", 0, 28, 29), - new Region("punctuation", 0, 30, 31)); + AssertSyntax(regions, code, 0, + Token("class", "keyword"), + Token("C1", "class name"), + Token("{", "punctuation"), + Token("string", "keyword"), + Token("s", "identifier"), + Token("=", "operator"), + Token("$\"", "string"), + Token("{", "punctuation"), + Token("5", "number"), + Token("}", "punctuation"), + Token("\"", "string"), + Token(";", "punctuation"), + Token("}", "punctuation")); } - private void ValidateRegions(IEnumerable regions, params Region[] expected) + private void AssertSyntax(IEnumerable regions, string code, int startLine, params TokenSpec[] expected) { var arr = regions.ToArray(); Assert.Equal(expected.Length, arr.Length); + var lineNo = startLine; + var lastIndex = 0; + var lines = Microsoft.CodeAnalysis.Text.SourceText.From(code).Lines; for (var i = 0; i < arr.Length; i++) { - var expect = expected[i]; - var result = arr[i]; + var tokenSpec = expected[i]; + var region = arr[i]; + string line; + int start, end; + do { + line = lines[lineNo].ToString(); + start = line.IndexOf(tokenSpec.Text, lastIndex); + if (start == -1) + { + if(++lineNo >= lines.Count) + { + throw new Exception($"Could not find token {tokenSpec.Text} in the code"); + } + + lastIndex = 0; + } + } while (start == -1); + end = start + tokenSpec.Text.Length; + lastIndex = end; - Assert.Equal(expect.Kind, result.Kind); - - Assert.Equal(expect.StartLine, result.Start.Line); - Assert.Equal(expect.StartChar, result.Start.Character); - - Assert.Equal(expect.EndLine, result.End.Line); - Assert.Equal(expect.EndChar, result.End.Character); + Assert.Equal(tokenSpec.Kind, region.Kind); + Assert.Equal(lineNo, region.Start.Line); + Assert.Equal(lineNo, region.End.Line); + Assert.Equal(start, region.Start.Character); + Assert.Equal(end, region.End.Character); } } - private class Region + private TokenSpec Token(string text, string kind) { - public int StartLine { get; } - public int StartChar { get; } - public int EndLine { get; } - public int EndChar { get; } + return new TokenSpec(kind, text); + } + private class TokenSpec + { + public string Text { get; } public string Kind { get; } - public Region(string kind, int line, int start, int end) - { - Kind = kind; - StartLine = EndLine = line; - StartChar = start; - EndChar = end; - } - - public Region(string kind, int startLine, int startChar, int endLine, int endChar) + public TokenSpec(string kind, string text) { Kind = kind; - StartLine = startLine; - EndLine = endLine; - StartChar = startChar; - EndChar = endChar; + Text = text; } } } From 49d1c7563cbb662bfe80d48432271eefcad59288 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 20 May 2015 09:22:04 -0400 Subject: [PATCH 11/22] Added support for multiple projects --- .../OmnisharpController.Highlighting.cs | 34 ++++++--- src/OmniSharp/Models/HighlightResponse.cs | 76 +++++++++++++++++-- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs index 2045b8d74d..34462f1ad0 100644 --- a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -1,7 +1,9 @@ +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; @@ -13,24 +15,38 @@ public partial class OmnisharpController [HttpPost("highlight")] public async Task> Highlight(HighlightRequest request) { - var document = _workspace.GetDocument(request.FileName); + var documents = _workspace.GetDocuments(request.FileName); + var results = new List>(); + + foreach (var document in documents) { + var spans = await this.GetHighlightSpan(request.Lines, document); + results.AddRange(spans); + } + + return results.GroupBy(z => z.Item1.TextSpan.ToString()).Select(a => HighlightResponse.FromClassifiedSpan(a.First().Item1, a.First().Item3, a.Select(z => z.Item2))); + } + + private async Task>> GetHighlightSpan(int[] Lines, Document document) + { + var results = new List>(); + var project = document.Project.Name; var model = await document.GetSemanticModelAsync(); var text = await document.GetTextAsync(); - - var results = new List(); - if (request.Lines == null || request.Lines.Length == 0) + + if (Lines == null || Lines.Length == 0) { - results.AddRange(Classifier.GetClassifiedSpans(model, new TextSpan(0, text.Length), _workspace)); + foreach (var span in Classifier.GetClassifiedSpans(model, new TextSpan(0, text.Length), _workspace)) + results.Add(Tuple.Create(span, project, text.Lines)); } else { - foreach (var line in request.Lines) + foreach (var line in Lines) { - results.AddRange(Classifier.GetClassifiedSpans(model, text.Lines[line].Span, _workspace)); + foreach (var span in Classifier.GetClassifiedSpans(model, text.Lines[line].Span, _workspace)) + results.Add(Tuple.Create(span, project, text.Lines)); } } - - return results.Select(s => HighlightResponse.FromClassifiedSpan(s, text.Lines)); + return results; } } } diff --git a/src/OmniSharp/Models/HighlightResponse.cs b/src/OmniSharp/Models/HighlightResponse.cs index 20f7accf30..6ab1615709 100644 --- a/src/OmniSharp/Models/HighlightResponse.cs +++ b/src/OmniSharp/Models/HighlightResponse.cs @@ -1,24 +1,90 @@ +using System; +using System.Collections.Generic; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Text; namespace OmniSharp.Models { - public class HighlightResponse + public class HighlightResponse : IComparable { public LinePosition Start { get; set; } public LinePosition End { get; set; } public string Kind { get; set; } - - internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLineCollection lines) + public IEnumerable Projects { get; set; } + + internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLineCollection lines, IEnumerable projects) { var linePos = lines.GetLinePositionSpan(span.TextSpan); - + return new HighlightResponse { Start = linePos.Start, End = linePos.End, - Kind = span.ClassificationType + Kind = span.ClassificationType, + Projects = projects }; } + + public int CompareTo(HighlightResponse other) + { + if (other.Start.Line < Start.Line) + { + return 1; + } + else if (other.Start.Line > Start.Line) + { + return -1; + } + // same start line + else if (other.Start.Character < Start.Character) + { + return 1; + } + else if (other.Start.Character > Start.Character) + { + return -1; + } + // same start line and start column + else if (other.End.Line < End.Line) + { + return 1; + } + else if (other.End.Line > End.Line) + { + return -1; + } + // same start line, start column, and end line + else if (other.End.Character < End.Character) + { + return 1; + } + else if (other.End.Character > End.Character) + { + return -1; + } + // same, same + else + { + return 0; + } + } + + public override bool Equals(object other) + { + var node = other as FileMemberElement; + return node != null + && node.Location.Line == Start.Line + && node.Location.Column == Start.Character + && node.Location.EndLine == End.Line + && node.Location.EndColumn == End.Character; + } + + public override int GetHashCode() + { + return 13 * Start.Line + + 17 * Start.Character + + 23 * End.Line + + 31 * End.Character; + } } } From 554d0d90b5118910f31f35d04fc6db129179b6c8 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Sat, 27 Jun 2015 18:09:13 -0400 Subject: [PATCH 12/22] Updated highlighting... need to fix splitting of language-csharp strings --- src/OmniSharp/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OmniSharp/Startup.cs b/src/OmniSharp/Startup.cs index d0380c461a..05390cbc5d 100644 --- a/src/OmniSharp/Startup.cs +++ b/src/OmniSharp/Startup.cs @@ -21,6 +21,7 @@ namespace OmniSharp { + // asdf public class Startup { public Startup() @@ -45,7 +46,6 @@ public Startup() } public IConfiguration Configuration { get; private set; } - public OmnisharpWorkspace Workspace { get; set; } public void ConfigureServices(IServiceCollection services) From 4172de2aac5c0740bfa94886436200723108f94b Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Sun, 28 Jun 2015 23:26:33 -0400 Subject: [PATCH 13/22] Updated highlighting to supress classifications that you don't need --- .../OmnisharpController.Highlighting.cs | 88 ++++++++++++++----- src/OmniSharp/Models/HighlightRequest.cs | 73 ++++++++++++++- 2 files changed, 135 insertions(+), 26 deletions(-) diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs index 34462f1ad0..407a07c1a7 100644 --- a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs @@ -16,37 +16,77 @@ public partial class OmnisharpController public async Task> Highlight(HighlightRequest request) { var documents = _workspace.GetDocuments(request.FileName); - var results = new List>(); + var results = new List(); - foreach (var document in documents) { - var spans = await this.GetHighlightSpan(request.Lines, document); - results.AddRange(spans); + foreach (var document in documents) + { + var project = document.Project.Name; + var text = await document.GetTextAsync(); + var spans = new List(); + + 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, spans) + .Select(span => new ClassifiedResult() + { + Span = span, + Lines = text.Lines, + Project = project + })); } - return results.GroupBy(z => z.Item1.TextSpan.ToString()).Select(a => HighlightResponse.FromClassifiedSpan(a.First().Item1, a.First().Item3, a.Select(z => z.Item2))); + return results + .GroupBy(z => z.Span.TextSpan.ToString()) + .Select(a => HighlightResponse.FromClassifiedSpan(a.First().Span, a.First().Lines, a.Select(z => z.Project))); + } + + class ClassifiedResult + { + public ClassifiedSpan Span { get; set; } + public TextLineCollection Lines { get; set; } + public string Project { get; set; } } - private async Task>> GetHighlightSpan(int[] Lines, Document document) + private IEnumerable FilterSpans(HighlightRequest request, IEnumerable spans) { - var results = new List>(); - var project = document.Project.Name; - var model = await document.GetSemanticModelAsync(); - var text = await document.GetTextAsync(); + if (!request.WantNames) + spans = spans.Where(x => !x.ClassificationType.EndsWith(" name")); + if (!request.WantComments) + spans = spans.Where(x => x.ClassificationType != "comment" && !x.ClassificationType.StartsWith("xml doc comment")); + if (!request.WantStrings) + spans = spans.Where(x => x.ClassificationType != "string"); + if (!request.WantOperators) + spans = spans.Where(x => x.ClassificationType != "operator"); + if (!request.WantPunctuation) + spans = spans.Where(x => x.ClassificationType != "punctuation"); + if (!request.WantKeywords) + spans = spans.Where(x => x.ClassificationType != "keyword"); + if (!request.WantNumbers) + spans = spans.Where(x => x.ClassificationType != "number"); + if (!request.WantIdentifiers) + spans = spans.Where(x => x.ClassificationType != "identifier"); + if (!request.WantPreprocessorKeywords) + spans = spans.Where(x => x.ClassificationType != "preprocessor keyword"); + if (!request.WantExcludedCode) + spans = spans.Where(x => x.ClassificationType != "excluded code"); - if (Lines == null || Lines.Length == 0) - { - foreach (var span in Classifier.GetClassifiedSpans(model, new TextSpan(0, text.Length), _workspace)) - results.Add(Tuple.Create(span, project, text.Lines)); - } - else - { - foreach (var line in Lines) - { - foreach (var span in Classifier.GetClassifiedSpans(model, text.Lines[line].Span, _workspace)) - results.Add(Tuple.Create(span, project, text.Lines)); - } - } - return results; + return spans; } } } diff --git a/src/OmniSharp/Models/HighlightRequest.cs b/src/OmniSharp/Models/HighlightRequest.cs index 2f6802adaf..d83ad12ab2 100644 --- a/src/OmniSharp/Models/HighlightRequest.cs +++ b/src/OmniSharp/Models/HighlightRequest.cs @@ -7,9 +7,78 @@ public class HighlightRequest : Request /// If none are given, highlight the entire /// file. /// + public int[] Lines { get; set; } + /// + /// Request comment classifications + /// /// - /// This is 0 indexed. + /// Defaults to true /// - public int[] Lines { get; set; } + public bool WantComments { get; set; } = true; + /// + /// Request string classifications + /// + /// + /// Defaults to true + /// + public bool WantStrings { get; set; } = true; + /// + /// Request operator classifications + /// + /// + /// Defaults to true + /// + public bool WantOperators { get; set; } = true; + /// + /// Request punctuation classifications + /// + /// + /// Defaults to true + /// + public bool WantPunctuation { get; set; } = true; + /// + /// Request keyword classifications + /// + /// + /// Defaults to true + /// + public bool WantKeywords { get; set; } = true; + /// + /// Request number classifications + /// + /// + /// Defaults to true + /// + public bool WantNumbers { get; set; } = true; + /// + /// Request name classifications + /// (interface, class, enum, etc) + /// + /// + /// Defaults to true + /// + public bool WantNames { get; set; } = true; + /// + /// Request identifier classifications + /// + /// + /// Defaults to true + /// + public bool WantIdentifiers { get; set; } = true; + /// + /// Request keyword classifications + /// + /// + /// Defaults to true + /// + public bool WantPreprocessorKeywords { get; set; } = true; + /// + /// Request excluded code keyword classifications + /// (eg. frameworks in a dnx solution) + /// + /// + /// Defaults to true + /// + public bool WantExcludedCode { get; set; } = true; } } From 5c565caadf33f8e3063aa41ee4b8424074237100 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 29 Jun 2015 14:21:02 -0400 Subject: [PATCH 14/22] Moved highlighting under v1 --- .../Api/{ => v1}/Highlighting/OmnisharpController.Highlighting.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/OmniSharp/Api/{ => v1}/Highlighting/OmnisharpController.Highlighting.cs (100%) diff --git a/src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs similarity index 100% rename from src/OmniSharp/Api/Highlighting/OmnisharpController.Highlighting.cs rename to src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs From 68b245fbef3d0dc5aef07e44a0f3cb1e1644322d Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 29 Jun 2015 14:26:38 -0400 Subject: [PATCH 15/22] Moved and modified highlight request/response --- .../Models/{ => v1}/HighlightRequest.cs | 0 .../Models/{ => v1}/HighlightResponse.cs | 44 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) rename src/OmniSharp/Models/{ => v1}/HighlightRequest.cs (100%) rename src/OmniSharp/Models/{ => v1}/HighlightResponse.cs (59%) diff --git a/src/OmniSharp/Models/HighlightRequest.cs b/src/OmniSharp/Models/v1/HighlightRequest.cs similarity index 100% rename from src/OmniSharp/Models/HighlightRequest.cs rename to src/OmniSharp/Models/v1/HighlightRequest.cs diff --git a/src/OmniSharp/Models/HighlightResponse.cs b/src/OmniSharp/Models/v1/HighlightResponse.cs similarity index 59% rename from src/OmniSharp/Models/HighlightResponse.cs rename to src/OmniSharp/Models/v1/HighlightResponse.cs index 6ab1615709..e720626e4d 100644 --- a/src/OmniSharp/Models/HighlightResponse.cs +++ b/src/OmniSharp/Models/v1/HighlightResponse.cs @@ -7,8 +7,10 @@ namespace OmniSharp.Models { public class HighlightResponse : IComparable { - public LinePosition Start { get; set; } - public LinePosition End { get; set; } + 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 Projects { get; set; } @@ -18,8 +20,10 @@ internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLi return new HighlightResponse { - Start = linePos.Start, - End = linePos.End, + StartLine = linePos.Start.Line + 1, + StartColumn = linePos.Start.Character + 1, + EndLine = linePos.Start.Line + 1, + EndColumn = linePos.Start.Character + 1, Kind = span.ClassificationType, Projects = projects }; @@ -27,38 +31,38 @@ internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLi public int CompareTo(HighlightResponse other) { - if (other.Start.Line < Start.Line) + if (other.StartLine < StartLine) { return 1; } - else if (other.Start.Line > Start.Line) + else if (other.StartLine > StartLine) { return -1; } // same start line - else if (other.Start.Character < Start.Character) + else if (other.StartColumn < StartColumn) { return 1; } - else if (other.Start.Character > Start.Character) + else if (other.StartColumn > StartColumn) { return -1; } // same start line and start column - else if (other.End.Line < End.Line) + else if (other.EndLine < EndLine) { return 1; } - else if (other.End.Line > End.Line) + else if (other.EndLine > EndLine) { return -1; } // same start line, start column, and end line - else if (other.End.Character < End.Character) + else if (other.EndColumn < EndColumn) { return 1; } - else if (other.End.Character > End.Character) + else if (other.EndColumn > EndColumn) { return -1; } @@ -73,18 +77,18 @@ public override bool Equals(object other) { var node = other as FileMemberElement; return node != null - && node.Location.Line == Start.Line - && node.Location.Column == Start.Character - && node.Location.EndLine == End.Line - && node.Location.EndColumn == End.Character; + && node.Location.Line == StartLine + && node.Location.Column == StartColumn + && node.Location.EndLine == EndLine + && node.Location.EndColumn == EndColumn; } public override int GetHashCode() { - return 13 * Start.Line + - 17 * Start.Character + - 23 * End.Line + - 31 * End.Character; + return 13 * StartLine + + 17 * StartColumn + + 23 * EndLine + + 31 * EndColumn; } } } From 83c7137629130beec3aa4dad3214c5555083b951 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 29 Jun 2015 15:28:53 -0400 Subject: [PATCH 16/22] Fixed up tests --- .../OmnisharpController.Highlighting.cs | 4 +- src/OmniSharp/Models/v1/HighlightResponse.cs | 7 +- tests/OmniSharp.Tests/HighlightFacts.cs | 145 +++++++++++++++--- 3 files changed, 131 insertions(+), 25 deletions(-) diff --git a/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs index 407a07c1a7..21cad88662 100644 --- a/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs @@ -68,9 +68,9 @@ private IEnumerable FilterSpans(HighlightRequest request, IEnume if (!request.WantNames) spans = spans.Where(x => !x.ClassificationType.EndsWith(" name")); if (!request.WantComments) - spans = spans.Where(x => x.ClassificationType != "comment" && !x.ClassificationType.StartsWith("xml doc comment")); + spans = spans.Where(x => x.ClassificationType != "comment" && !x.ClassificationType.StartsWith("xml doc comment ")); if (!request.WantStrings) - spans = spans.Where(x => x.ClassificationType != "string"); + spans = spans.Where(x => x.ClassificationType != "string" && !x.ClassificationType.StartsWith("string ")); if (!request.WantOperators) spans = spans.Where(x => x.ClassificationType != "operator"); if (!request.WantPunctuation) diff --git a/src/OmniSharp/Models/v1/HighlightResponse.cs b/src/OmniSharp/Models/v1/HighlightResponse.cs index e720626e4d..8de9b385ec 100644 --- a/src/OmniSharp/Models/v1/HighlightResponse.cs +++ b/src/OmniSharp/Models/v1/HighlightResponse.cs @@ -21,9 +21,10 @@ internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLi return new HighlightResponse { StartLine = linePos.Start.Line + 1, - StartColumn = linePos.Start.Character + 1, - EndLine = linePos.Start.Line + 1, - EndColumn = linePos.Start.Character + 1, + EndLine = linePos.End.Line + 1, + // Caution, Character is 1-based. + StartColumn = linePos.Start.Character, + EndColumn = linePos.End.Character, Kind = span.ClassificationType, Projects = projects }; diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index 898b990222..ec02bb1f21 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -18,15 +18,15 @@ namespace N1 class C1 { int n = true; } } "; - + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { { "a.cs", code } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 3 } }); - + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 4 } }); + AssertSyntax(regions, code, 3, Token("class", "keyword"), Token("C1", "class name"), @@ -48,7 +48,7 @@ namespace N1 class C1 { int n = true; } } "; - + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { { "a.cs", code } @@ -72,7 +72,7 @@ class C1 { int n = true; } Token("}", "punctuation"), Token("}", "punctuation")); } - + [Fact] public async Task HighlightStringInterpolation() { @@ -82,15 +82,15 @@ class C1 string s = $""{5}""; } "; - + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { { "a.cs", code } }); - + var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); - + AssertSyntax(regions, code, 0, Token("class", "keyword"), Token("C1", "class name"), @@ -106,16 +106,15 @@ class C1 Token(";", "punctuation"), Token("}", "punctuation")); } - + private void AssertSyntax(IEnumerable regions, string code, int startLine, params TokenSpec[] expected) { var arr = regions.ToArray(); - Assert.Equal(expected.Length, arr.Length); - + var lineNo = startLine; var lastIndex = 0; var lines = Microsoft.CodeAnalysis.Text.SourceText.From(code).Lines; - for (var i = 0; i < arr.Length; i++) + for (var i = 0; i < arr.Length; i++) { var tokenSpec = expected[i]; var region = arr[i]; @@ -130,21 +129,127 @@ private void AssertSyntax(IEnumerable regions, string code, i { throw new Exception($"Could not find token {tokenSpec.Text} in the code"); } - + lastIndex = 0; } } while (start == -1); end = start + tokenSpec.Text.Length; lastIndex = end; - + Assert.Equal(tokenSpec.Kind, region.Kind); - Assert.Equal(lineNo, region.Start.Line); - Assert.Equal(lineNo, region.End.Line); - Assert.Equal(start, region.Start.Character); - Assert.Equal(end, region.End.Character); + Assert.Equal(lineNo, region.StartLine - 1); + Assert.Equal(lineNo, region.EndLine - 1); + Assert.Equal(start, region.StartColumn); + Assert.Equal(end, region.EndColumn); } + Assert.Equal(expected.Length, arr.Length); } - + + [Fact] + public async Task HighlightExcludesUnwantedKeywords() + { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", code } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantKeywords = false }); + + Assert.DoesNotContain(regions, x => x.Kind == "keyword"); + } + + [Fact] + public async Task HighlightExcludesUnwantedPunctuation() + { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", code } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantPunctuation = false }); + + Assert.DoesNotContain(regions, x => x.Kind == "punctuation"); + } + + [Fact] + public async Task HighlightExcludesUnwantedOperators() + { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", code } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantOperators = false }); + + Assert.DoesNotContain(regions, x => x.Kind == "operator"); + } + + [Fact] + public async Task HighlightExcludesUnwantedIdentifiers() + { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", code } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantIdentifiers = false }); + + Assert.DoesNotContain(regions, x => x.Kind == "identifier"); + } + + [Fact] + public async Task HighlightExcludesUnwantedNames() + { + var code = @" + namespace N1 + { + class C1 { int n = true; } + } + "; + + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary + { + { "a.cs", code } + }); + + var controller = new OmnisharpController(workspace, null); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantNames = false }); + + Assert.DoesNotContain(regions, x => x.Kind.EndsWith(" name")); + } + private TokenSpec Token(string text, string kind) { return new TokenSpec(kind, text); @@ -153,7 +258,7 @@ private class TokenSpec { public string Text { get; } public string Kind { get; } - + public TokenSpec(string kind, string text) { Kind = kind; From 0f5f3fbc196312c63ae3ba3ec647815a42df3a7e Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 29 Jun 2015 15:48:28 -0400 Subject: [PATCH 17/22] Fixed changes in gitignore, removed temporary comment --- .gitignore | 2 +- src/OmniSharp/Startup.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7810000c40..0d12a6283a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,6 @@ nuget.exe *.ipch *.sln.ide debugSettings.json -/.vs buildlog +/.vs *.lock.json diff --git a/src/OmniSharp/Startup.cs b/src/OmniSharp/Startup.cs index 05390cbc5d..025e15f142 100644 --- a/src/OmniSharp/Startup.cs +++ b/src/OmniSharp/Startup.cs @@ -21,7 +21,6 @@ namespace OmniSharp { - // asdf public class Startup { public Startup() From b4f38d990371fe22228afc0a9998f57c196e2271 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 29 Jun 2015 15:49:30 -0400 Subject: [PATCH 18/22] Fixed line formatting in startup.cs --- src/OmniSharp/Startup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OmniSharp/Startup.cs b/src/OmniSharp/Startup.cs index 025e15f142..d4a37344a2 100644 --- a/src/OmniSharp/Startup.cs +++ b/src/OmniSharp/Startup.cs @@ -45,6 +45,7 @@ public Startup() } public IConfiguration Configuration { get; private set; } + public OmnisharpWorkspace Workspace { get; set; } public void ConfigureServices(IServiceCollection services) From 8e5a68e7aebf35ef3cc4675fb73f0c54235949dc Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 29 Jun 2015 15:58:10 -0400 Subject: [PATCH 19/22] React to multi-project filtering --- .../Api/v1/Highlighting/OmnisharpController.Highlighting.cs | 5 +++++ src/OmniSharp/Models/v1/HighlightRequest.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs index 21cad88662..0f61806d6c 100644 --- a/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs @@ -16,6 +16,11 @@ public partial class OmnisharpController public async Task> 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)); + } + var results = new List(); foreach (var document in documents) diff --git a/src/OmniSharp/Models/v1/HighlightRequest.cs b/src/OmniSharp/Models/v1/HighlightRequest.cs index d83ad12ab2..a53baab0d6 100644 --- a/src/OmniSharp/Models/v1/HighlightRequest.cs +++ b/src/OmniSharp/Models/v1/HighlightRequest.cs @@ -8,6 +8,11 @@ public class HighlightRequest : Request /// file. /// public int[] Lines { get; set; } + /// + /// Specifies which projects to highlight for. + // If none are given, highlight for all the projects. + /// + public string[] ProjectNames { get; set; } /// /// Request comment classifications /// From 1b296103af4e3088804928c543e168396ca7742e Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Tue, 30 Jun 2015 08:37:18 -0400 Subject: [PATCH 20/22] React to PR feedback --- .../OmnisharpController.Highlighting.cs | 63 ++++++++----- src/OmniSharp/Models/v1/Highlight.cs | 94 +++++++++++++++++++ .../Models/v1/HighlightClassification.cs | 21 +++++ src/OmniSharp/Models/v1/HighlightRequest.cs | 76 ++------------- src/OmniSharp/Models/v1/HighlightResponse.cs | 92 +----------------- tests/OmniSharp.Tests/HighlightFacts.cs | 18 ++-- 6 files changed, 169 insertions(+), 195 deletions(-) create mode 100644 src/OmniSharp/Models/v1/Highlight.cs create mode 100644 src/OmniSharp/Models/v1/HighlightClassification.cs diff --git a/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs index 0f61806d6c..cd09d9281b 100644 --- a/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs +++ b/src/OmniSharp/Api/v1/Highlighting/OmnisharpController.Highlighting.cs @@ -13,7 +13,7 @@ namespace OmniSharp public partial class OmnisharpController { [HttpPost("highlight")] - public async Task> Highlight(HighlightRequest request) + public async Task Highlight(HighlightRequest request) { var documents = _workspace.GetDocuments(request.FileName); if (request.ProjectNames != null && request.ProjectNames.Length > 0) @@ -21,6 +21,16 @@ public async Task> Highlight(HighlightRequest req 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(); foreach (var document in documents) @@ -47,7 +57,7 @@ public async Task> Highlight(HighlightRequest req } } - results.AddRange(FilterSpans(request, spans) + results.AddRange(FilterSpans(request.Classifications, spans) .Select(span => new ClassifiedResult() { Span = span, @@ -56,9 +66,13 @@ public async Task> Highlight(HighlightRequest req })); } - return results - .GroupBy(z => z.Span.TextSpan.ToString()) - .Select(a => HighlightResponse.FromClassifiedSpan(a.First().Span, a.First().Lines, a.Select(z => z.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 @@ -68,28 +82,25 @@ class ClassifiedResult public string Project { get; set; } } - private IEnumerable FilterSpans(HighlightRequest request, IEnumerable spans) + private HighlightClassification[] AllClassifications = Enum.GetValues(typeof(HighlightClassification)).Cast().ToArray(); + + private IEnumerable FilterSpans(HighlightClassification[] classifications, IEnumerable spans) { - if (!request.WantNames) - spans = spans.Where(x => !x.ClassificationType.EndsWith(" name")); - if (!request.WantComments) - spans = spans.Where(x => x.ClassificationType != "comment" && !x.ClassificationType.StartsWith("xml doc comment ")); - if (!request.WantStrings) - spans = spans.Where(x => x.ClassificationType != "string" && !x.ClassificationType.StartsWith("string ")); - if (!request.WantOperators) - spans = spans.Where(x => x.ClassificationType != "operator"); - if (!request.WantPunctuation) - spans = spans.Where(x => x.ClassificationType != "punctuation"); - if (!request.WantKeywords) - spans = spans.Where(x => x.ClassificationType != "keyword"); - if (!request.WantNumbers) - spans = spans.Where(x => x.ClassificationType != "number"); - if (!request.WantIdentifiers) - spans = spans.Where(x => x.ClassificationType != "identifier"); - if (!request.WantPreprocessorKeywords) - spans = spans.Where(x => x.ClassificationType != "preprocessor keyword"); - if (!request.WantExcludedCode) - spans = spans.Where(x => x.ClassificationType != "excluded code"); + 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; } diff --git a/src/OmniSharp/Models/v1/Highlight.cs b/src/OmniSharp/Models/v1/Highlight.cs new file mode 100644 index 0000000000..782f38d04c --- /dev/null +++ b/src/OmniSharp/Models/v1/Highlight.cs @@ -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 + { + 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 Projects { get; set; } + + internal static HighlightSpan FromClassifiedSpan(ClassifiedSpan span, TextLineCollection lines, IEnumerable 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; + } + } +} diff --git a/src/OmniSharp/Models/v1/HighlightClassification.cs b/src/OmniSharp/Models/v1/HighlightClassification.cs new file mode 100644 index 0000000000..84f0b89f60 --- /dev/null +++ b/src/OmniSharp/Models/v1/HighlightClassification.cs @@ -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 + } +} diff --git a/src/OmniSharp/Models/v1/HighlightRequest.cs b/src/OmniSharp/Models/v1/HighlightRequest.cs index a53baab0d6..130681f52b 100644 --- a/src/OmniSharp/Models/v1/HighlightRequest.cs +++ b/src/OmniSharp/Models/v1/HighlightRequest.cs @@ -13,77 +13,13 @@ public class HighlightRequest : Request // If none are given, highlight for all the projects. /// public string[] ProjectNames { get; set; } - /// - /// Request comment classifications - /// - /// - /// Defaults to true - /// - public bool WantComments { get; set; } = true; - /// - /// Request string classifications - /// - /// - /// Defaults to true - /// - public bool WantStrings { get; set; } = true; - /// - /// Request operator classifications - /// - /// - /// Defaults to true - /// - public bool WantOperators { get; set; } = true; - /// - /// Request punctuation classifications - /// - /// - /// Defaults to true - /// - public bool WantPunctuation { get; set; } = true; - /// - /// Request keyword classifications - /// - /// - /// Defaults to true - /// - public bool WantKeywords { get; set; } = true; - /// - /// Request number classifications - /// - /// - /// Defaults to true - /// - public bool WantNumbers { get; set; } = true; - /// - /// Request name classifications - /// (interface, class, enum, etc) - /// - /// - /// Defaults to true - /// - public bool WantNames { get; set; } = true; - /// - /// Request identifier classifications - /// - /// - /// Defaults to true - /// - public bool WantIdentifiers { get; set; } = true; - /// - /// Request keyword classifications + /// + /// Request specific classifications, if none are requested you will get them all. /// - /// - /// Defaults to true - /// - public bool WantPreprocessorKeywords { get; set; } = true; - /// - /// Request excluded code keyword classifications - /// (eg. frameworks in a dnx solution) + public HighlightClassification[] Classifications { get; set; } + /// + /// Exclude specific classifications /// - /// - /// Defaults to true - /// - public bool WantExcludedCode { get; set; } = true; + public HighlightClassification[] ExcludeClassifications { get; set; } } } diff --git a/src/OmniSharp/Models/v1/HighlightResponse.cs b/src/OmniSharp/Models/v1/HighlightResponse.cs index 8de9b385ec..6d4b7a8d86 100644 --- a/src/OmniSharp/Models/v1/HighlightResponse.cs +++ b/src/OmniSharp/Models/v1/HighlightResponse.cs @@ -1,95 +1,7 @@ -using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Text; - namespace OmniSharp.Models { - public class HighlightResponse : IComparable + public class HighlightResponse { - 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 Projects { get; set; } - - internal static HighlightResponse FromClassifiedSpan(ClassifiedSpan span, TextLineCollection lines, IEnumerable projects) - { - var linePos = lines.GetLinePositionSpan(span.TextSpan); - - return new HighlightResponse - { - StartLine = linePos.Start.Line + 1, - EndLine = linePos.End.Line + 1, - // Caution, Character is 1-based. - StartColumn = linePos.Start.Character, - EndColumn = linePos.End.Character, - Kind = span.ClassificationType, - Projects = projects - }; - } - - public int CompareTo(HighlightResponse 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; - } + public HighlightSpan[] Highlights { get; set; } } } diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index ec02bb1f21..a78b211853 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -27,7 +27,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", Lines = new[] { 4 } }); - AssertSyntax(regions, code, 3, + AssertSyntax(regions.Highlights, code, 3, Token("class", "keyword"), Token("C1", "class name"), Token("{", "punctuation"), @@ -57,7 +57,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); - AssertSyntax(regions, code, 0, + AssertSyntax(regions.Highlights, code, 0, Token("namespace", "keyword"), Token("N1", "identifier"), Token("{", "punctuation"), @@ -91,7 +91,7 @@ class C1 var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs" }); - AssertSyntax(regions, code, 0, + AssertSyntax(regions.Highlights, code, 0, Token("class", "keyword"), Token("C1", "class name"), Token("{", "punctuation"), @@ -107,7 +107,7 @@ class C1 Token("}", "punctuation")); } - private void AssertSyntax(IEnumerable regions, string code, int startLine, params TokenSpec[] expected) + private void AssertSyntax(IEnumerable regions, string code, int startLine, params TokenSpec[] expected) { var arr = regions.ToArray(); @@ -163,7 +163,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantKeywords = false }); - Assert.DoesNotContain(regions, x => x.Kind == "keyword"); + Assert.DoesNotContain(regions.Highlights, x => x.Kind == "keyword"); } [Fact] @@ -184,7 +184,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantPunctuation = false }); - Assert.DoesNotContain(regions, x => x.Kind == "punctuation"); + Assert.DoesNotContain(regions.Highlights, x => x.Kind == "punctuation"); } [Fact] @@ -205,7 +205,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantOperators = false }); - Assert.DoesNotContain(regions, x => x.Kind == "operator"); + Assert.DoesNotContain(regions.Highlights, x => x.Kind == "operator"); } [Fact] @@ -226,7 +226,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantIdentifiers = false }); - Assert.DoesNotContain(regions, x => x.Kind == "identifier"); + Assert.DoesNotContain(regions.Highlights, x => x.Kind == "identifier"); } [Fact] @@ -247,7 +247,7 @@ class C1 { int n = true; } var controller = new OmnisharpController(workspace, null); var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantNames = false }); - Assert.DoesNotContain(regions, x => x.Kind.EndsWith(" name")); + Assert.DoesNotContain(regions.Highlights, x => x.Kind.EndsWith(" name")); } private TokenSpec Token(string text, string kind) From 1583948203c2747b70501ba29abedaf5ab3c887a Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Tue, 30 Jun 2015 15:36:21 -0400 Subject: [PATCH 21/22] Fixed tests --- tests/OmniSharp.Tests/HighlightFacts.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index a78b211853..a6133ba936 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -18,7 +18,7 @@ namespace N1 class C1 { int n = true; } } "; - + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { { "a.cs", code } @@ -139,8 +139,8 @@ private void AssertSyntax(IEnumerable regions, string code, int s Assert.Equal(tokenSpec.Kind, region.Kind); Assert.Equal(lineNo, region.StartLine - 1); Assert.Equal(lineNo, region.EndLine - 1); - Assert.Equal(start, region.StartColumn); - Assert.Equal(end, region.EndColumn); + Assert.Equal(start, region.StartColumn - 1); + Assert.Equal(end, region.EndColumn - 1); } Assert.Equal(expected.Length, arr.Length); } @@ -161,13 +161,13 @@ class C1 { int n = true; } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantKeywords = false }); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", ExcludeClassifications = new [] { HighlightClassification.Keyword } }); Assert.DoesNotContain(regions.Highlights, x => x.Kind == "keyword"); } [Fact] - public async Task HighlightExcludesUnwantedPunctuation() + public async Task HighlightExclude sUnwantedPunctuation() { var code = @" namespace N1 @@ -182,7 +182,7 @@ class C1 { int n = true; } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantPunctuation = false }); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", ExcludeClassifications = new [] { HighlightClassification.Punctuation } }); Assert.DoesNotContain(regions.Highlights, x => x.Kind == "punctuation"); } @@ -203,7 +203,7 @@ class C1 { int n = true; } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantOperators = false }); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", ExcludeClassifications = new [] { HighlightClassification.Operator } }); Assert.DoesNotContain(regions.Highlights, x => x.Kind == "operator"); } @@ -224,7 +224,7 @@ class C1 { int n = true; } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantIdentifiers = false }); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", ExcludeClassifications = new [] { HighlightClassification.Identifier } }); Assert.DoesNotContain(regions.Highlights, x => x.Kind == "identifier"); } @@ -245,7 +245,7 @@ class C1 { int n = true; } }); var controller = new OmnisharpController(workspace, null); - var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", WantNames = false }); + var regions = await controller.Highlight(new HighlightRequest() { FileName = "a.cs", ExcludeClassifications = new [] { HighlightClassification.Name } }); Assert.DoesNotContain(regions.Highlights, x => x.Kind.EndsWith(" name")); } From 53b16a939b11191333ae2118707cc7f8762081fe Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Tue, 30 Jun 2015 15:49:05 -0400 Subject: [PATCH 22/22] Really fixing broken tests --- tests/OmniSharp.Tests/HighlightFacts.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/OmniSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Tests/HighlightFacts.cs index a6133ba936..be9b810efb 100644 --- a/tests/OmniSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Tests/HighlightFacts.cs @@ -18,7 +18,7 @@ namespace N1 class C1 { int n = true; } } "; - + var workspace = TestHelpers.CreateSimpleWorkspace(new Dictionary { { "a.cs", code } @@ -167,7 +167,7 @@ class C1 { int n = true; } } [Fact] - public async Task HighlightExclude sUnwantedPunctuation() + public async Task HighlightExcludesUnwantedPunctuation() { var code = @" namespace N1