diff --git a/README.md b/README.md index 7536ed4..0eadc2a 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/package.json b/package.json index 8e2203e..0b81d27 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/extension.js b/src/extension.js index 3278137..1a6efdf 100644 --- a/src/extension.js +++ b/src/extension.js @@ -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 -}; \ No newline at end of file + activate, + deactivate, +};