Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Change Log

## [Unreleased]
- Improve file deletion detection.
- Modernize internal codebase and update dependencies.

## [0.10.0] - 2025-01-05

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022-2025 Andrey Babanin
Copyright (c) 2022-2026 Andrey Babanin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# zserio Language Support for Visual Studio Code
# zserio Language Support

This extension provides [zserio](https://github.com/ndsev/zserio) schema language support. The following features are supported:
A lightweight Visual Studio Code extension providing language support for [zserio](https://github.com/ndsev/zserio) schema files (`.zs`).

- Syntax checking
- Go to definition and peek definition
- Symbol search
- Basic autocomplete support
## Features

- **Syntax Highlighting**: Built-in grammar for `.zs` files.
- **Syntax Checking**: Real-time schema validation and error reporting.
- **Code Navigation**: Support for *Go to Definition* and *Peek Definition*.
- **Symbol Search**: Quickly find document and workspace symbols.
- **Autocomplete**: Basic context-aware completion suggestions.

## Issues and Contributions

Found a bug or have a feature request? Please open an issue on the [GitHub repository](https://github.com/4og/zserio-language-support).
1,774 changes: 1,024 additions & 750 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,18 @@
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@eslint/js": "^9.18.0",
"@stylistic/eslint-plugin": "^2.13.0",
"@types/node": "^22.17.0",
"@eslint/js": "^10.0.1",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/node": "^24.12.0",
"@types/vscode": "^1.106.0",
"@typescript-eslint/eslint-plugin": "^8.19.0",
"@typescript-eslint/parser": "^8.19.0",
"@vscode/test-cli": "^0.0.11",
"@typescript-eslint/eslint-plugin": "^8.57.0",
"@typescript-eslint/parser": "^8.57.0",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.6.0",
"eslint": "^9.17.0",
"typescript": "^5.7.2",
"typescript-eslint": "^8.21.0"
"@vscode/vsce": "^3.7.1",
"eslint": "^10.0.3",
"typescript": "^5.9.3",
"typescript-eslint": "^8.57.0"
},
"dependencies": {
"antlr4": "^4.13.2"
Expand Down
6 changes: 3 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function activate(context: vscode.ExtensionContext) {
[languageId], new CompletionItemProvider(parsedDocumentCollection));
context.subscriptions.push(completionProvider);

if (vscode.window.activeTextEditor) {
if (vscode.window.activeTextEditor?.document) {
parsedDocumentCollection.parseDocument(vscode.window.activeTextEditor.document);
}

Expand All @@ -46,13 +46,13 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(document => parsedDocumentCollection.parseDocument(document)));

context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
if (editor?.document) {
parsedDocumentCollection.parseDocument(editor.document);
}
}));

context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(editor => {
if (editor) {
if (editor?.document) {
parsedDocumentCollection.parseDocument(editor.document);
}
}));
Expand Down
7 changes: 1 addition & 6 deletions src/parser/entityReference.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import * as vscode from 'vscode';

export class EntityReference {
constructor(name: string, range: vscode.Range) {
this.name = name;
this.range = range;
}
name: string;
range: vscode.Range;
constructor(public name: string, public range: vscode.Range) {}
}
33 changes: 12 additions & 21 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,29 @@ import { TypeReferenceVisitor } from './typeReferenceVisitor';
import { SyntaxErrorListener } from './syntaxErrorListener';

export class ParsedDocument {
constructor(version: number, tree: ParserRuleContext, packageName: string | undefined, symbols: vscode.DocumentSymbol[], docStrings: Map<vscode.DocumentSymbol, vscode.MarkdownString>, references: EntityReference[], imports: EntityReference[]) {
this.version = version;
this.tree = tree;
this.packageName = packageName;
this.symbols = symbols;
this.docStrings = docStrings;
this.references = references;
this.imports = imports;
}
version: number;
tree: ParserRuleContext;
packageName?: string;
symbols: vscode.DocumentSymbol[];
docStrings: Map<vscode.DocumentSymbol, vscode.MarkdownString>;
references: EntityReference[];
imports: EntityReference[];
constructor(
public version: number,
public tree: ParserRuleContext,
public packageName: string | undefined,
public symbols: vscode.DocumentSymbol[],
public docStrings: Map<vscode.DocumentSymbol, vscode.MarkdownString>,
public references: EntityReference[],
public imports: EntityReference[]
) {}
}

export class ParsedDocumentCollection {
constructor(diagnostic_collection: vscode.DiagnosticCollection) {
this.diagnosticCollection = diagnostic_collection;
constructor(public diagnosticCollection: vscode.DiagnosticCollection) {
this.parsedDocuments = new Map<string, ParsedDocument>();
this.completionKeywords = ZserioLexer.literalNames.filter((name: string | null) => name && name.match(/.*\w.*/) != null).map((name: string) => name.slice(1, -1));
this.completionKeywords = ZserioLexer.literalNames.filter((name: string | null) => name?.match(/.*\w.*/)).map((name: string) => name.slice(1, -1));
}

diagnosticCollection: vscode.DiagnosticCollection;
parsedDocuments: Map<string, ParsedDocument>;
completionKeywords: string[];

async getParsedDocument(document: vscode.TextDocument): Promise<ParsedDocument> {
const parsedDocument = this.parsedDocuments.get(document.uri.toString());
if (parsedDocument && parsedDocument.version == document.version) {
if (parsedDocument?.version === document.version) {
return parsedDocument;
}
const pd = this.parseZserioDocument(document, this.diagnosticCollection);
Expand Down
11 changes: 4 additions & 7 deletions src/parser/symbolDeclarationsVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ class BaseZserioParserVisitor extends ZserioParserVisitor<void> {
markdownCommentRegex = /(^\s*\/\*!|!\*\/$)/gm;

createSymbol(ctxId: IdContext, ctxWhole: ParserRuleContext, detail: string, kind: vscode.SymbolKind, childrenVisitor?: BaseZserioParserVisitor): vscode.DocumentSymbol {
let name = ctxId.getText();
if (!name) {
name = "<invalid>";
}
const name = ctxId.getText() ?? "<invalid>";
const range = convertCompleteRange(ctxWhole.start, ctxWhole.stop);
let selectionRange = convertRange(ctxId.start);

Expand Down Expand Up @@ -78,13 +75,13 @@ class EnumLikeVisitor extends BaseZserioParserVisitor {
class StructLikeVisitor extends BaseZserioParserVisitor {
override visitStructureFieldDefinition = (ctx: StructureFieldDefinitionContext) => {
const fieldTypeId = ctx.fieldTypeId();
if (fieldTypeId !== null) {
if (fieldTypeId) {
this.symbols.push(this.createSymbol(fieldTypeId.id(), ctx, "", vscode.SymbolKind.Field));
}
};
override visitChoiceFieldDefinition = (ctx: ChoiceFieldDefinitionContext) => {
const fieldTypeId = ctx.fieldTypeId();
if (fieldTypeId !== null) {
if (fieldTypeId) {
this.symbols.push(this.createSymbol(fieldTypeId.id(), ctx, "", vscode.SymbolKind.Field));
}
};
Expand All @@ -102,7 +99,7 @@ class StructLikeVisitor extends BaseZserioParserVisitor {
};
override visitFunctionDefinition = (ctx: FunctionDefinitionContext) => {
const functionName = ctx.functionName();
if (functionName !== null) {
if (functionName) {
this.symbols.push(this.createSymbol(functionName.id(), ctx, "", vscode.SymbolKind.Function));
}
};
Expand Down
4 changes: 1 addition & 3 deletions src/parser/syntaxErrorListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import { ErrorListener, Token, Recognizer, RecognitionException } from 'antlr4';
import * as vscode from 'vscode';

export class SyntaxErrorListener extends ErrorListener<Token> {
constructor(diagnostics: vscode.Diagnostic[]) {
constructor(public diagnostics: vscode.Diagnostic[]) {
super();
this.diagnostics = diagnostics;
}
diagnostics: vscode.Diagnostic[];
override syntaxError(recognizer: Recognizer<Token>, offendingSymbol: Token, line: number, column: number, msg: string, _e: RecognitionException | undefined): void {
const length = offendingSymbol.stop - offendingSymbol.start + 1;
this.diagnostics.push({
Expand Down
4 changes: 1 addition & 3 deletions src/providers/completionItemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import { ParsedDocumentCollection } from '../parser/parser';
import { locateImportByQualifiedName } from '../parser/utils';

export default class CompletionItemProvider implements vscode.CompletionItemProvider {
constructor(parsedDocumentCollection: ParsedDocumentCollection) {
this.parsedDocumentCollection = parsedDocumentCollection;
constructor(private parsedDocumentCollection: ParsedDocumentCollection) {
this.keywords = parsedDocumentCollection.completionKeywords.map((name: string): vscode.CompletionItem => { return { label: name, kind: vscode.CompletionItemKind.Keyword }; });
}

parsedDocumentCollection: ParsedDocumentCollection;
keywords: vscode.CompletionItem[];

async provideCompletionItems(document: vscode.TextDocument, _position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> {
Expand Down
5 changes: 1 addition & 4 deletions src/providers/definitionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ import { ParsedDocumentCollection, ParsedDocument } from '../parser/parser';
import { locateImportByQualifiedName } from '../parser/utils';

export default class DefinitionProvider implements vscode.DefinitionProvider {
constructor(parsedDocumentCollection: ParsedDocumentCollection) {
this.parsedDocumentCollection = parsedDocumentCollection;
constructor(private parsedDocumentCollection: ParsedDocumentCollection) {
}

parsedDocumentCollection: ParsedDocumentCollection;

async provideDefinition(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.Definition> {

const parsedDocument = await this.parsedDocumentCollection.getParsedDocument(document);
Expand Down
5 changes: 1 addition & 4 deletions src/providers/documentSemanticTokensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ import * as vscode from 'vscode';
import { ParsedDocumentCollection } from '../parser/parser';

export default class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensProvider {
constructor(parsedDocumentCollection: ParsedDocumentCollection) {
this.parsedDocumentCollection = parsedDocumentCollection;
constructor(private parsedDocumentCollection: ParsedDocumentCollection) {
}

private parsedDocumentCollection: ParsedDocumentCollection;

async provideDocumentSemanticTokens(document: vscode.TextDocument, _token: vscode.CancellationToken): Promise<vscode.SemanticTokens> {
const parsedDocument = await this.parsedDocumentCollection.getParsedDocument(document);
const builder = new vscode.SemanticTokensBuilder(DocumentSemanticTokensProvider.getLegend());
Expand Down
5 changes: 1 addition & 4 deletions src/providers/documentSymbolProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ import * as vscode from 'vscode';
import { ParsedDocumentCollection } from '../parser/parser';

export default class DocumentSymbolProvider implements vscode.DocumentSymbolProvider {
constructor(parsedDocumentCollection: ParsedDocumentCollection) {
this.parsedDocumentCollection = parsedDocumentCollection;
constructor(private parsedDocumentCollection: ParsedDocumentCollection) {
}

parsedDocumentCollection: ParsedDocumentCollection;

async provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken):
Promise<vscode.SymbolInformation[] | vscode.DocumentSymbol[]> {
return (await this.parsedDocumentCollection.getParsedDocument(document)).symbols;
Expand Down
Loading