Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental Semantic Highlighter #3667

Merged
merged 10 commits into from
May 11, 2020

Conversation

JoeRobich
Copy link
Member

@JoeRobich JoeRobich commented Mar 15, 2020

Implementation of the Semantic Tokens provider (microsoft/vscode#86415) requested in #3565. Built on the Semantic Endpoint added to OmniSharp in OmniSharp/omnisharp-roslyn#1734

Future enhancement:

  • Add RegEx classifications

With Textmate Classification:
image

With Semantic Classification:
image

With Semantic Classification and Visual Studio 2019 theme tweaks:
image

Applied theme tweaks:
    "editor.tokenColorCustomizations": {
        "[Default Dark+]": {
            "textMateRules": [
                {
                    "scope": "entity.name.namespace",
                    "settings": {
                        "foreground": "#D4D4D4"
                    }
                },
                {
                    "scope": "entity.name.variable.field",
                    "settings": {
                        "foreground": "#D4D4D4"
                    }
                },
                {
                    "scope": "variable.other.property",
                    "settings": {
                        "foreground": "#D4D4D4"
                    }
                },
                {
                    "scope": "variable.other.constant",
                    "settings": {
                        "foreground": "#D4D4D4",
                    }
                },
                {
                    "scope": "variable.other.enummember",
                    "settings": {
                        "foreground": "#D4D4D4"
                    }
                },
                {
                    "scope": "entity.name.type.interface",
                    "settings": {
                        "foreground": "#b8d7a3"
                    }
                },
                {
                    "scope": "entity.name.type.enum",
                    "settings": {
                        "foreground": "#b8d7a3"
                    }
                },
                {
                    "scope": "entity.name.type.parameter",
                    "settings": {
                        "foreground": "#b8d7a3"
                    }
                },
                {
                    "scope": "entity.name.type.struct",
                    "settings": {
                        "foreground": "#86C691",
                    }
                },
                {
                    "scope": "entity.name.function.extension",
                    "settings": {
                        "foreground": "#DCDCAA",
                    }
                },
                {
                    "scope": "comment.documentation",
                    "settings": {
                        "foreground": "#608B4E",
                    }
                },
                {
                    "scope": "comment.documentation.attribute",
                    "settings": {
                        "foreground": "#C8C8C8",
                    }
                },
                {
                    "scope": "comment.documentation.cdata",
                    "settings": {
                        "foreground": "#E9D585",
                    }
                },
                {
                    "scope": "comment.documentation.delimiter",
                    "settings": {
                        "foreground": "#808080",
                    }
                },
                {
                    "scope": "comment.documentation.name",
                    "settings": {
                        "foreground": "#569CD6",
                    }
                },
                {
                    "scope": "comment.regex",
                    "settings": {
                        "foreground": "#57A64A",
                    }
                },
                {
                    "scope": "constant.character.character-class.regexp",
                    "settings": {
                        "foreground": "#2EABFE",
                    }
                },
                {
                    "scope": "keyword.control.anchor.regexp",
                    "settings": {
                        "foreground": "#F979AE",
                    }
                },
                {
                    "scope": "keyword.operator.quantifier.regexp",
                    "settings": {
                        "foreground": "#F979AE",
                    }
                },
                {
                    "scope": "punctuation.definition.group.regexp",
                    "settings": {
                        "foreground": "#05C3BA",
                    }
                },
                {
                    "scope": "keyword.operator.or.regexp",
                    "settings": {
                        "foreground": "#05C3BA",
                    }
                },
                {
                    "scope": "string.regexp",
                    "settings": {
                        "foreground": "#D69D85",
                    }
                },
                {
                    "scope": "constant.character.escape.regexp",
                    "settings": {
                        "foreground": "#D69D85",
                    }
                },
                {
                    "scope": "constant.other.escape.regexp",
                    "settings": {
                        "foreground": "#FFD68F",
                    }
                },
            ]
        },
        "[Default Light+]": {
            "textMateRules": [
                {
                    "scope": "entity.name.namespace",
                    "settings": {
                        "foreground": "#222222"
                    }
                },
                {
                    "scope": "entity.name.variable.field",
                    "settings": {
                        "foreground": "#222222"
                    }
                },
                {
                    "scope": "variable.other.property",
                    "settings": {
                        "foreground": "#222222"
                    }
                },
                {
                    "scope": "variable.other.constant",
                    "settings": {
                        "foreground": "#222222",
                    }
                },
                {
                    "scope": "variable.other.enummember",
                    "settings": {
                        "foreground": "#222222"
                    }
                },
                {
                    "scope": "entity.name.type.interface",
                    "settings": {
                        "foreground": "#267f99"
                    }
                },
                {
                    "scope": "entity.name.type.enum",
                    "settings": {
                        "foreground": "#267f99"
                    }
                },
                {
                    "scope": "entity.name.type.parameter",
                    "settings": {
                        "foreground": "#267f99"
                    }
                },
                {
                    "scope": "entity.name.type.struct",
                    "settings": {
                        "foreground": "#267f99",
                    }
                },
                {
                    "scope": "entity.name.function.extension",
                    "settings": {
                        "foreground": "#795E26",
                    }
                },
                {
                    "scope": "comment.documentation",
                    "settings": {
                        "foreground": "#008000",
                    }
                },
                {
                    "scope": "comment.documentation.attribute",
                    "settings": {
                        "foreground": "#282828",
                    }
                },
                {
                    "scope": "comment.documentation.cdata",
                    "settings": {
                        "foreground": "#808080",
                    }
                },
                {
                    "scope": "comment.documentation.delimiter",
                    "settings": {
                        "foreground": "#808080",
                    }
                },
                {
                    "scope": "comment.documentation.name",
                    "settings": {
                        "foreground": "#808080",
                    }
                }
            ]
        }
    },

@NTaylorMullen
Copy link
Contributor

@ryanbrandenburg since you own our semantic classification you should take a look at this as well. It will also enable us to call through to Roslyn for semantic colorization as well 😄

@codecov
Copy link

codecov bot commented Mar 16, 2020

Codecov Report

Merging #3667 into master will increase coverage by 0.02%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3667      +/-   ##
==========================================
+ Coverage   87.01%   87.03%   +0.02%     
==========================================
  Files          59       59              
  Lines        1779     1782       +3     
  Branches      207      207              
==========================================
+ Hits         1548     1551       +3     
  Misses        176      176              
  Partials       55       55              
Flag Coverage Δ
#integration 100.00% <ø> (ø)
#unit 87.03% <100.00%> (+0.02%) ⬆️
Impacted Files Coverage Δ
src/omnisharp/options.ts 94.31% <100.00%> (+0.13%) ⬆️
src/omnisharp/protocol.ts 77.34% <100.00%> (+0.17%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 65168f8...a2fb93e. Read the comment docs.

src/omnisharp/extension.ts Outdated Show resolved Hide resolved
const semanticSpans = response.Spans;

const builder = new vscode.SemanticTokensBuilder();
for (let span of semanticSpans) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm misunderstanding the code but why not have the server just return the LSP compatible type vs. re-building it here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just following the typescript extension as an example where they request information from their server then perform some post processing. I'll have to think more on whether to keep it like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, TypeScript hasn't moved fully over to LSP yet which is why they follow that model. They plan to move fully over to LSP

src/features/semanticTokensProvider.ts Show resolved Hide resolved
export default class SemanticTokensProvider extends AbstractProvider implements vscode.DocumentSemanticTokensProvider, vscode.DocumentRangeSemanticTokensProvider {

getLegend(): vscode.SemanticTokensLegend {
return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server's typically the one that defines the legend. Why re-create it on the client (vs. querying it)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah goes back to whether the OmniSharp endpoint should just implement the LSP return types. I'll think on it.

@JoeRobich
Copy link
Member Author

@NTaylorMullen Would like to include this in the next release. We'll consider it experimental (defaulted off) and collect feedback about the performance. Ultimately we may implement an API that is closer to the LSP, but that may wait until Roslyn implements a classification service that returns tokens and modifiers.

@JoeRobich JoeRobich removed the Blocked label May 10, 2020
Copy link
Contributor

@filipw filipw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@NTaylorMullen
Copy link
Contributor

Ultimately we may implement an API that is closer to the LSP, but that may wait until Roslyn implements a classification service that returns tokens and modifiers.

Ah, when that comes to fruition that'll enable us to add semantic C# colorization to Razor's C# pieces. Is that something that's tracked somewhere? FYI @ryanbrandenburg

We'll consider it experimental (defaulted off) and collect feedback about the performance.

What type of switch do you plan to put it behind? Asking because our TagHelper semantic highlighting is cheap and is something we'd have "on" all the time however if we were to semantically highlight our C# pieces of the document we'd want it behind a similar switch to ensure it's only active when yours is active.

@JoeRobich JoeRobich force-pushed the semantic-highlight branch 2 times, most recently from 89beb40 to a00eec9 Compare May 11, 2020 20:03
@JoeRobich JoeRobich force-pushed the semantic-highlight branch from a00eec9 to a2fb93e Compare May 11, 2020 20:13
@JoeRobich
Copy link
Member Author

Is that something that's tracked somewhere?

We didn't have an exact issue tracking this, so I made a proposal. dotnet/roslyn#44155

What type of switch do you plan to put it behind?

There is a csharp.semanticHighlighting.enabled setting which is available off of the Options class.

@JoeRobich
Copy link
Member Author

Although GitHub checks didn't update, this is the passing Travis CI for this PR - https://travis-ci.org/github/OmniSharp/omnisharp-vscode/builds/685827799

Merging

@JoeRobich JoeRobich merged commit 4b249d1 into dotnet:master May 11, 2020
@JoeRobich
Copy link
Member Author

for good measure this is the passing master build following this merge https://travis-ci.org/github/OmniSharp/omnisharp-vscode/builds/685844034

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants