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: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
"${workspaceFolder}/dist/*.js"
],
}
]
Expand Down
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,21 @@ This extension provides comprehensive syntax highlighting for Device Tree Source

*Example of Device Tree file with syntax highlighting enabled*

#### Supported File Extensions
### Formatting

This extension provides intelligent formatting for Device Tree Source (DTS) files with support for:

- **Automatic indentation**: Proper nesting levels for nodes, properties, and blocks
- **Line length management**: Configurable maximum line length with smart wrapping
- **Comment preservation**: Maintains inline and block comments during formatting
- **Whitespace normalization**: Consistent spacing around operators and delimiters
- **Property alignment**: Organized layout for property definitions and cell arrays

![Device Tree Formatting](docs/images/formatting.gif)

*Example of Device Tree file before and after formatting*

### Supported File Extensions

- `.dts` - Device Tree Source files
- `.dtsi` - Device Tree Source Include files
Expand All @@ -34,15 +48,11 @@ Install this extension from the [Visual Studio Code Marketplace](https://marketp

## Commands

### `DeviceTree: Hello World`

Displays a welcome message (placeholder command for future DeviceTree functionality).

### `DeviceTree: Validate Syntax` *(Coming Soon)*

Validates DeviceTree syntax and highlights errors.

### `DeviceTree: Format Document` *(Coming Soon)*
### `DeviceTree: Format Document`

Formats DeviceTree files according to standard conventions.

Expand Down Expand Up @@ -77,7 +87,6 @@ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guid
- Describe the use case and expected functionality
- Include examples of DeviceTree syntax that should be supported


## Changelog

Everything related to versions and their release notes can be found in the [CHANGELOG.md](CHANGELOG.md).
Expand Down
Binary file added docs/images/formatting.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/highlighting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 21 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,28 @@
"watch-tests": "tsc -p . -w --outDir out"
},
"contributes": {
"commands": [
"configurationDefaults": {
"[dts]": {
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 8
}
},
"configuration": [
{
"command": "devicetree.helloWorld",
"title": "Hello World"
"title": "DeviceTree Language",
"properties": {
"devicetree.maxLineLength": {
"type": "number",
"default": 80,
"description": "Maximum line length for DeviceTree files. Lines longer than this will be wrapped."
},
"devicetree.enableWarnings": {
"type": "boolean",
"default": true,
"description": "Enable warnings for DeviceTree files. If disabled, only errors will be reported."
}
}
}
],
"grammars": [
Expand Down
106 changes: 106 additions & 0 deletions src/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as vscode from 'vscode';

/**
* Provider for DeviceTree diagnostic warnings
* Manages diagnostic warnings for DeviceTree files, such as line length issues
*/
export class DtsDiagnosticsProvider {
private diagnosticCollection: vscode.DiagnosticCollection;
private maxLineLength: number;
private tabSize: number;

constructor(maxLineLength: number) {
this.diagnosticCollection = vscode.languages.createDiagnosticCollection('devicetree');
this.maxLineLength = maxLineLength + 1;

// Read tabSize from editor configuration
const editorConfig = vscode.workspace.getConfiguration('editor');
this.tabSize = editorConfig.get<number>('tabSize', 8);
}

/**
* Calculate visual length of a line considering tab stops
* @param line The line to calculate the visual length for
* @returns The visual length of the line
*/
private calculateVisualLength(line: string): number {
let visualLength = 0;
for (const char of line) {
if (char === '\t') {
// Move to next tab stop
visualLength += this.tabSize - (visualLength % this.tabSize);
} else {
visualLength += 1;
}
}
return visualLength + 1;
}

/**
* Check for lines exceeding maximum length
* @param document The document to check
* @returns Array of diagnostics for lines exceeding maximum length
*/
private checkLineLength(document: vscode.TextDocument): vscode.Diagnostic[] {
const diagnostics: vscode.Diagnostic[] = [];
const text = document.getText();
const lines = text.split('\n');

lines.forEach((line, index) => {
const visualLength = this.calculateVisualLength(line);
if (visualLength > this.maxLineLength) {
const range = new vscode.Range(index, 0, index, Number.MAX_VALUE);
const diagnostic = new vscode.Diagnostic(
range,
`Line exceeds maximum length of ${this.maxLineLength} characters (current: ${visualLength})`,
vscode.DiagnosticSeverity.Warning
);
diagnostic.source = 'DeviceTree';
diagnostics.push(diagnostic);
}
});

return diagnostics;
}

/**
* Analyze a document and update diagnostics
* @param document The document to analyze
*/
public analyzeDocument(document: vscode.TextDocument): void {
if (document.languageId !== 'dts') {
return;
}

// Update tabSize in case it has changed
const editor = vscode.window.visibleTextEditors.find(
e => e.document.uri.toString() === document.uri.toString()
);

if (editor) {
this.tabSize = editor.options.tabSize as number;
}

const diagnostics: vscode.Diagnostic[] = [];

// Run all diagnostic checks, currently only line length
diagnostics.push(...this.checkLineLength(document));

this.diagnosticCollection.set(document.uri, diagnostics);
}

/**
* Clear diagnostics for a specific document
* @param document The document to clear diagnostics for
*/
public clearDocument(document: vscode.TextDocument): void {
this.diagnosticCollection.delete(document.uri);
}

/**
* Dispose the diagnostic collection
*/
public dispose(): void {
this.diagnosticCollection.dispose();
}
}
95 changes: 79 additions & 16 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,89 @@
'use strict';

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
// Import the formatter and diagnostics providers
import { DtsFormatterProvider } from './formatter';
import { DtsDiagnosticsProvider } from './diagnostics';

// Global diagnostics provider instance
let diagnosticsProvider: DtsDiagnosticsProvider | undefined;

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
/**
* Activate the extension
* This method is called when your extension is activated
* @param context The extension context
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function activate(context: vscode.ExtensionContext) {
// Get configuration
const config = vscode.workspace.getConfiguration('devicetree');
const maxLineLength = config.get<number>('maxLineLength', 80);
const enableWarnings = config.get<boolean>('enableWarnings', true);

// Create diagnostics provider
diagnosticsProvider = new DtsDiagnosticsProvider(maxLineLength);

// Register the formatter provider
context.subscriptions.push(
vscode.languages.registerDocumentFormattingEditProvider('dts', new DtsFormatterProvider(maxLineLength))
);

// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "devicetree" is now active!');
// Register diagnostics provider
context.subscriptions.push(diagnosticsProvider);
if (enableWarnings) {
// Listen for document changes
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument(event => {
if (event.document.languageId === 'dts' && diagnosticsProvider) {
// Debounce: analyze after a short delay to avoid excessive analysis
setTimeout(() => {
diagnosticsProvider?.analyzeDocument(event.document);
}, 500);
}
})
);

// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const disposable = vscode.commands.registerCommand('devicetree.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from DeviceTree!');
});
// Listen for text editor options changes (e.g., when tab size changes in the editor)
context.subscriptions.push(
vscode.window.onDidChangeTextEditorOptions(event => {
const document = event.textEditor.document;
if (document.languageId === 'dts' && diagnosticsProvider) {
diagnosticsProvider.analyzeDocument(document);
}
})
);

context.subscriptions.push(disposable);
// Listen for document opens
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(document => {
if (document.languageId === 'dts' && diagnosticsProvider) {
diagnosticsProvider.analyzeDocument(document);
}
})
);

// Listen for document closes
context.subscriptions.push(
vscode.workspace.onDidCloseTextDocument(document => {
if (document.languageId === 'dts' && diagnosticsProvider) {
diagnosticsProvider.clearDocument(document);
}
})
);

}
}

// This method is called when your extension is deactivated
export function deactivate() { }
/**
* Deactivate the extension
* This method is called when your extension is deactivated
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function deactivate() {
if (diagnosticsProvider) {
diagnosticsProvider.dispose();
diagnosticsProvider = undefined;
}
}
Loading