Skip to content

Commit

Permalink
rework semantic highlighting to use cursor finders
Browse files Browse the repository at this point in the history
  • Loading branch information
antico5 committed Nov 11, 2023
1 parent c41db43 commit fcd61ba
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 55 deletions.
28 changes: 0 additions & 28 deletions server/src/parser/slangHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { NodeType, RuleNode, TokenNode } from "@nomicfoundation/slang/cst";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { TextRange } from "@nomicfoundation/slang/text_index";
import _ from "lodash";
Expand All @@ -19,33 +18,6 @@ export interface SlangNodeWrapper {
pathRuleNodes: SlangNode[];
}

export function walk(
cursor: Cursor,
onEnter: NodeCallback,
onExit: NodeCallback
) {
const node = cursor.node;

const nodeWrapper: SlangNodeWrapper = {
textRange: cursor.textRange,
type: node.type,
kind: node.kind,
text: node.text,
pathRuleNodes: cursor.pathRuleNodes,
};

onEnter(nodeWrapper);

if (nodeWrapper.type === NodeType.Rule) {
for (let i = 0; i < node.children.length; i++) {
cursor.goToNthChild(i);
walk(cursor, onEnter, onExit);
}
}
onExit(nodeWrapper);
cursor.goToParent();
}

export function slangToVSCodeRange(
doc: TextDocument,
slangRange: TextRange
Expand Down
4 changes: 3 additions & 1 deletion server/src/services/semanticHighlight/HighlightVisitor.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { TextDocument } from "vscode-languageserver-textdocument";
import { TokenKind } from "@nomicfoundation/slang/kinds";
import { SlangNodeWrapper } from "../../parser/slangHelpers";
import { SemanticTokensBuilder } from "./SemanticTokensBuilder";

// Abstraction for a visitor that wants to highlight tokens
export abstract class HighlightVisitor {
public abstract tokenKinds: Set<TokenKind>;

constructor(
public document: TextDocument,
public tokenBuilder: SemanticTokensBuilder
) {}

public enter(nodeWrapper: SlangNodeWrapper): void {}
public exit(nodeWrapper: SlangNodeWrapper): void {}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/ContractDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights contract definitions
export class ContractDefinitionHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/CustomTypeHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights custom type names
export class CustomTypeHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/EventDefinitionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights event definitions
export class EventDefinitionHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/EventEmissionHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights event emissions
export class EventEmissionHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'TokenNode' is defined but never used. Allowed unused vars must match /^_/u
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 4 in server/src/services/semanticHighlight/highlighters/FunctionCallHighlighter.ts

View workflow job for this annotation

GitHub Actions / build (windows-latest)

'Cursor' is defined but never used. Allowed unused vars must match /^_/u
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights function calls
export class FunctionCallHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights function definitions
export class FunctionDefinitionHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights interface definitions
export class InterfaceDefinitionHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { TokenKind } from "@nomicfoundation/slang/kinds";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";
Expand Down Expand Up @@ -91,6 +91,8 @@ const keywordKinds = new Set([

// Highlights keywords
export class KeywordHighlighter extends HighlightVisitor {
public tokenKinds = keywordKinds;

public enter(nodeWrapper: SlangNodeWrapper): void {
if (
nodeWrapper.type === NodeType.Token &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { TokenKind } from "@nomicfoundation/slang/kinds";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

Expand All @@ -12,6 +13,8 @@ const numberKinds = new Set([

// Highlights numbers
export class NumberHighlighter extends HighlightVisitor {
public tokenKinds = numberKinds;

public enter(nodeWrapper: SlangNodeWrapper): void {
if (
nodeWrapper.type === NodeType.Token &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { TokenKind } from "@nomicfoundation/slang/kinds";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

Expand All @@ -12,6 +13,8 @@ const stringKinds = new Set([

// Highlights strings
export class StringHighlighter extends HighlightVisitor {
public tokenKinds = stringKinds;

public enter(nodeWrapper: SlangNodeWrapper): void {
if (
nodeWrapper.type === NodeType.Token &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { SemanticTokenTypes } from "vscode-languageserver-protocol";
import { NodeType } from "@nomicfoundation/slang/cst";
import { NodeType, TokenNode } from "@nomicfoundation/slang/cst";
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { HighlightVisitor } from "../HighlightVisitor";
import { SlangNodeWrapper } from "../../../parser/slangHelpers";

// Highlights struct definitions
export class StructDefinitionHighlighter extends HighlightVisitor {
public tokenKinds = new Set([TokenKind.Identifier]);

public enter(nodeWrapper: SlangNodeWrapper): void {
const ancestors = nodeWrapper.pathRuleNodes;
if (
Expand Down
54 changes: 39 additions & 15 deletions server/src/services/semanticHighlight/onSemanticTokensFull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import {
SemanticTokens,
SemanticTokensParams,
} from "vscode-languageserver-protocol";
import _ from "lodash";
import _, { Dictionary } from "lodash";
import { analyze } from "@nomicfoundation/solidity-analyzer";
import semver from "semver";
import { Language } from "@nomicfoundation/slang/language";
import { ProductionKind } from "@nomicfoundation/slang/kinds";
import { ProductionKind, TokenKind } from "@nomicfoundation/slang/kinds";
import { Cursor } from "@nomicfoundation/slang/cursor";
import { TokenNode } from "@nomicfoundation/slang/cst";
import { ServerState } from "../../types";
import { walk } from "../../parser/slangHelpers";
import { CustomTypeHighlighter } from "./highlighters/CustomTypeHighlighter";
import { SemanticTokensBuilder } from "./SemanticTokensBuilder";
import { KeywordHighlighter } from "./highlighters/KeywordHighlighter";
Expand All @@ -24,6 +25,7 @@ import { EventDefinitionHighlighter } from "./highlighters/EventDefinitionHighli
import { ContractDefinitionHighlighter } from "./highlighters/ContractDefinitionHighlighter";
import { InterfaceDefinitionHighlighter } from "./highlighters/InterfaceDefinitionHighlighter";
import { StructDefinitionHighlighter } from "./highlighters/StructDefinitionHighlighter";
import { HighlightVisitor } from "./HighlightVisitor";

const emptyResponse: SemanticTokens = { data: [] };

Expand Down Expand Up @@ -103,20 +105,42 @@ export function onSemanticTokensFull(serverState: ServerState) {
];

// Visit the CST
span = transaction.startChild({ op: "walk-highlight-tokens" });
walk(
parseTree.cursor,
(nodeWrapper) => {
for (const visitor of visitors) {
visitor.enter(nodeWrapper);
}
},
(nodeWrapper) => {
for (const visitor of visitors) {
visitor.exit(nodeWrapper);
const indexedVisitors: Dictionary<HighlightVisitor[]> = {};
const registeredTokenKinds: TokenKind[] = [];

for (const visitor of visitors) {
for (const tokenKind of visitor.tokenKinds) {
indexedVisitors[tokenKind] ||= [];
indexedVisitors[tokenKind].push(visitor);

if (!registeredTokenKinds.includes(tokenKind)) {
registeredTokenKinds.push(tokenKind);
}
}
);
}

const cursor: Cursor = parseTree.cursor;
let node: TokenNode;

span = transaction.startChild({ op: "walk-highlight-tokens" });
while (
(node = cursor.findTokenWithKind(registeredTokenKinds)) !== null
) {
const nodeWrapper = {
kind: node.kind,
pathRuleNodes: cursor.pathRuleNodes,
text: node.text,
textRange: cursor.textRange,
type: node.type,
};

const registeredVisitors = indexedVisitors[nodeWrapper.kind];
for (const visitor of registeredVisitors) {
visitor.enter(nodeWrapper);
}

cursor.goToNext();
}
span.finish();

return { status: "ok", result: { data: builder.getTokenData() } };
Expand Down

0 comments on commit fcd61ba

Please sign in to comment.