Add configurable comment characters for assembly

Introduces new settings for customizing line and block comment characters via VS Code configuration. Updates the extension to dynamically apply these settings and documents the feature in the README. Also bumps the minimum VS Code engine version and extension version.
This commit is contained in:
kapitche 2025-07-02 14:44:53 -04:00
parent 0d0cc1dd84
commit 43fb2d4a78
3 changed files with 227 additions and 91 deletions

View file

@ -49,6 +49,8 @@ This extension provides syntax highlighting for:
## Customization 🎨
### Syntax Highlighting Colors
You can customize the colors used for syntax highlighting by modifying your VS Code color theme. Add or modify entries in your `settings.json` file under `"editor.tokenColorCustomizations"`.
For example:
@ -72,6 +74,25 @@ For example:
}
```
### Comment Characters
You can configure which characters are used for comments to match your assembler. Add these settings to your VS Code `settings.json`:
```json
{
"assembly.comments.lineComment": "#",
"assembly.comments.blockComment": ["/*", "*/"]
}
```
**Common configurations:**
- **GNU Assembler (as)**: Use `#` for line comments
- **NASM/MASM**: Use `;` for line comments (default)
- **Custom assemblers**: Set to any character or string you prefer
The extension will automatically update comment behavior when you change these settings.
## Contributing 🤝
If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on the GitHub repository.

View file

@ -2,18 +2,43 @@
"name": "x86-assembly-syntax",
"displayName": "Universal Assembly Syntax Highlighting",
"description": "Syntax highlighting extension for x86, x64 and ARM assembly both for Intel and AT&T syntax",
"version": "1.1.0",
"version": "1.2.0",
"engines": {
"vscode": "^1.60.0"
"vscode": "^1.75.0"
},
"categories": [
"Programming Languages"
],
"main": "./src/extension.js",
"activationEvents": [
"onLanguage:x86asm"
],
"contributes": {
"configuration": {
"title": "Assembly Language",
"properties": {
"assembly.comments.lineComment": {
"type": "string",
"default": ";",
"description": "Character(s) used for line comments",
"examples": [
";",
"#",
"//"
]
},
"assembly.comments.blockComment": {
"type": "array",
"default": [
"/*",
"*/"
],
"description": "Characters used for block comments [start, end]",
"items": {
"type": "string"
},
"minItems": 2,
"maxItems": 2
}
}
},
"languages": [
{
"id": "x86asm",

View file

@ -1,122 +1,212 @@
const vscode = require('vscode');
const axios = require('axios');
const cheerio = require('cheerio');
const list = require('./mnemonics.js');
const vscode = require("vscode");
const axios = require("axios");
const cheerio = require("cheerio");
const list = require("./mnemonics.js");
let languageConfigDisposable = null;
function activate(context) {
// Hover provider for mnemonics
const hoverProvider = vscode.languages.registerHoverProvider('x86asm', {
async provideHover(document, position, token) {
const range = document.getWordRangeAtPosition(position, /\b[a-zA-Z0-9_]+\b/);
const word = document.getText(range);
// Set up initial language configuration
updateLanguageConfiguration();
if (word) {
// Check if the word is a valid mnemonic
if (list.x86x64Mnemonics.includes(word.toUpperCase())) {
const instructionInfo = await fetchInstructionInfo(word.toLowerCase());
if (instructionInfo) {
return new vscode.Hover(instructionInfo);
}
} else {
return undefined;
}
}
// Listen for configuration changes
const configChangeListener = vscode.workspace.onDidChangeConfiguration(
(event) => {
if (event.affectsConfiguration("assembly.comments")) {
updateLanguageConfiguration();
}
}
);
return undefined;
// Hover provider for mnemonics
const hoverProvider = vscode.languages.registerHoverProvider("x86asm", {
async provideHover(document, position, token) {
const range = document.getWordRangeAtPosition(
position,
/\b[a-zA-Z0-9_]+\b/
);
const word = document.getText(range);
if (word) {
// Check if the word is a valid mnemonic
if (list.x86x64Mnemonics.includes(word.toUpperCase())) {
const instructionInfo = await fetchInstructionInfo(
word.toLowerCase()
);
if (instructionInfo) {
return new vscode.Hover(instructionInfo);
}
} else {
return undefined;
}
});
}
// Definition provider (Ctrl+Click to jump)
const definitionProvider = vscode.languages.registerDefinitionProvider('x86asm', {
async provideDefinition(document, position, token) {
const range = document.getWordRangeAtPosition(position);
if (!range) {
return;
}
return undefined;
},
});
const word = document.getText(range);
return await findDefinition(word);
// Definition provider (Ctrl+Click to jump)
const definitionProvider = vscode.languages.registerDefinitionProvider(
"x86asm",
{
async provideDefinition(document, position, token) {
const range = document.getWordRangeAtPosition(position);
if (!range) {
return;
}
});
context.subscriptions.push(hoverProvider, definitionProvider);
const word = document.getText(range);
return await findDefinition(word);
},
}
);
context.subscriptions.push(
hoverProvider,
definitionProvider,
configChangeListener
);
}
function updateLanguageConfiguration() {
// Dispose previous language configuration if it exists
if (languageConfigDisposable) {
languageConfigDisposable.dispose();
}
// Get current configuration
const config = vscode.workspace.getConfiguration("assembly.comments");
const lineComment = config.get("lineComment", ";");
const blockComment = config.get("blockComment", ["/*", "*/"]);
// Register new language configuration
languageConfigDisposable = vscode.languages.setLanguageConfiguration(
"x86asm",
{
comments: {
lineComment: lineComment,
blockComment: blockComment,
},
brackets: [
["{", "}"],
["[", "]"],
["(", ")"],
],
autoClosingPairs: [
["{", "}"],
["[", "]"],
["(", ")"],
['"', '"'],
["'", "'"],
],
surroundingPairs: [
["{", "}"],
["[", "]"],
["(", ")"],
['"', '"'],
["'", "'"],
],
folding: {
markers: {
start: "\\.section",
end: "\\.endsection",
},
},
}
);
}
async function fetchInstructionInfo(instruction) {
try {
const url = `https://www.felixcloutier.com/x86/${instruction}`;
const response = await axios.get(url);
const $ = cheerio.load(response.data);
try {
const url = `https://www.felixcloutier.com/x86/${instruction}`;
const response = await axios.get(url);
const $ = cheerio.load(response.data);
// Extract the title (instruction name)
const instructionTitle = $('h1').text();
// Extract the title (instruction name)
const instructionTitle = $("h1").text();
// Extract Opcode table and relevant details
const opcodeTable = $('table').first();
const opcode = opcodeTable.find('tr').eq(1).find('td').eq(0).text();
const instructionDescription = opcodeTable.find('tr').eq(1).find('td').eq(5).text();
// Extract Opcode table and relevant details
const opcodeTable = $("table").first();
const opcode = opcodeTable.find("tr").eq(1).find("td").eq(0).text();
const instructionDescription = opcodeTable
.find("tr")
.eq(1)
.find("td")
.eq(5)
.text();
// Extract the operation section
const operationSection = $('#operation').next('pre').text();
// Extract the operation section
const operationSection = $("#operation").next("pre").text();
// Extract the description section
const descriptionSection = $('#description').nextUntil('h2', 'p').text();
// Extract the description section
const descriptionSection = $("#description").nextUntil("h2", "p").text();
// Combine the extracted information into a formatted output
let hoverContent = `**${instructionTitle}**\n\n`;
hoverContent += `**Opcode:** ${opcode}\n`;
hoverContent += `**Description:** ${instructionDescription}\n\n`;
hoverContent += `**Details:**\n${descriptionSection}\n\n`;
hoverContent += `**Operation:**\n${operationSection}`;
// Combine the extracted information into a formatted output
let hoverContent = `**${instructionTitle}**\n\n`;
hoverContent += `**Opcode:** ${opcode}\n`;
hoverContent += `**Description:** ${instructionDescription}\n\n`;
hoverContent += `**Details:**\n${descriptionSection}\n\n`;
hoverContent += `**Operation:**\n${operationSection}`;
return hoverContent;
} catch (error) {
if (error.response && error.response.status === 404) {
console.warn(`Instruction info not found for: ${instruction}`);
} else {
console.error(`Failed to fetch instruction info: ${error.message}`);
}
return null; // Return null if fetching fails
return hoverContent;
} catch (error) {
if (error.response && error.response.status === 404) {
console.warn(`Instruction info not found for: ${instruction}`);
} else {
console.error(`Failed to fetch instruction info: ${error.message}`);
}
return null; // Return null if fetching fails
}
}
async function findDefinition(word) {
// Get all x86 assembly files in the workspace
const files = await vscode.workspace.findFiles('**/*.s', '**/node_modules/**');
// Get all x86 assembly files in the workspace
const files = await vscode.workspace.findFiles(
"**/*.s",
"**/node_modules/**"
);
for (const file of files) {
const document = await vscode.workspace.openTextDocument(file);
const text = document.getText();
const lines = text.split('\n');
for (const file of files) {
const document = await vscode.workspace.openTextDocument(file);
const text = document.getText();
const lines = text.split("\n");
// Check for definitions in each file
const location = searchForDefinition(lines, word, document);
if (location) {
return location;
}
// Check for definitions in each file
const location = searchForDefinition(lines, word, document);
if (location) {
return location;
}
}
// If no definition is found, return undefined
return undefined;
// If no definition is found, return undefined
return undefined;
}
function searchForDefinition(lines, word, document) {
const externRegex = new RegExp(`^extern\\s+${word}\\s*`, 'i');
const globalRegex = new RegExp(`^global\\s+${word}\\s*`, 'i');
const labelRegex = new RegExp(`^${word}:`, 'i');
const externRegex = new RegExp(`^extern\\s+${word}\\s*`, "i");
const globalRegex = new RegExp(`^global\\s+${word}\\s*`, "i");
const labelRegex = new RegExp(`^${word}:`, "i");
for (let i = 0; i < lines.length; i++) {
if (externRegex.test(lines[i]) || globalRegex.test(lines[i]) || labelRegex.test(lines[i])) {
return new vscode.Location(document.uri, new vscode.Position(i, 0));
}
for (let i = 0; i < lines.length; i++) {
if (
externRegex.test(lines[i]) ||
globalRegex.test(lines[i]) ||
labelRegex.test(lines[i])
) {
return new vscode.Location(document.uri, new vscode.Position(i, 0));
}
}
return undefined; // If no match is found
return undefined; // If no match is found
}
function deactivate() {}
function deactivate() {
if (languageConfigDisposable) {
languageConfigDisposable.dispose();
}
}
module.exports = {
activate,
deactivate
};
activate,
deactivate,
};