before rewriting formatter

This commit is contained in:
2025-06-25 08:34:56 -06:00
parent c4a2e5a077
commit 889c150b08
5 changed files with 542 additions and 296 deletions

View File

@@ -1,71 +1,9 @@
# format-4690 README
This is the README for your extension "format-4690". After writing up a brief description, we recommend including the following sections.
## Features
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
For example if there is an image subfolder under your extension project workspace:
\!\[feature X\]\(images/feature-x.png\)
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
## Requirements
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
## Extension Settings
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
For example:
This extension contributes the following settings:
* `myExtension.enable`: Enable/disable this extension.
* `myExtension.thing`: Set to `blah` to do something.
## Known Issues
Calling out known issues can help limit users opening duplicate issues against your extension.
## Release Notes
Users appreciate release notes as you update your extension.
### 1.0.0
Initial release of ...
### 1.0.1
Fixed issue #.
### 1.1.0
Added features X, Y, and Z.
Hola
---
## Following extension guidelines
### 2025.06.01
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
## Working with Markdown
You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux).
* Toggle preview (`Shift+Cmd+V` on macOS or `Shift+Ctrl+V` on Windows and Linux).
* Press `Ctrl+Space` (Windows, Linux, macOS) to see a list of Markdown snippets.
## For more information
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
**Enjoy!**
Inicial

View File

@@ -23,5 +23,6 @@
["(", ")"],
["\"", "\""],
["'", "'"]
]
],
"wordPattern": "(\\?|[a-zA-Z])([a-zA-Z0-9#\\.]*)[$#%]?"
}

View File

@@ -4,12 +4,42 @@ interface FunctionDefinition {
name: string;
location: vscode.Location;
type: 'FUNCTION' | 'SUB' | 'DEF';
parameters: string[]; // Array of parameter names
parameterCount: number; // Number of parameters
}
export function activate(context: vscode.ExtensionContext) {
// Global symbol index - maps lowercase function names to their definitions
const symbolIndex = new Map<string, FunctionDefinition[]>();
// File index - maps file URIs to their function definitions
const fileIndex = new Map<string, FunctionDefinition[]>();
export async function activate(context: vscode.ExtensionContext) {
const selector: vscode.DocumentSelector = { language: '4690basic' };
// Existing formatter provider
const saveWatcher = vscode.workspace.onDidSaveTextDocument(async (document) => {
if (document.languageId === '4690basic') {
console.log(`File saved: ${document.uri.toString()}`);
await updateIndexForFile(document.uri);
}
});
const fsWatcher = vscode.workspace.createFileSystemWatcher('**/*.{bas,BAS,j86,J86}');
fsWatcher.onDidCreate(async (uri) => {
console.log(`File created: ${uri.toString()}`);
await updateIndexForFile(uri);
});
fsWatcher.onDidDelete((uri) => {
console.log(`File deleted: ${uri.toString()}`);
removeFromIndex(uri);
});
// Build initial symbol index
await buildInitialSymbolIndex();
// Existing formatter provider (unchanged)
const formatterProvider: vscode.DocumentFormattingEditProvider = {
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] {
const config = vscode.workspace.getConfiguration('4690basic.format');
@@ -44,10 +74,23 @@ export function activate(context: vscode.ExtensionContext) {
// Helper function to check if a line should continue
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
const trimmedLine = line.trim();
// Skip comment lines that start with backslash (like \REM!!)
// These should not be treated as continuation lines
if (/^\s*\\(REM|rem)/i.test(trimmedLine)) {
return false;
}
const withoutComments = removeComments(line).trim();
// Check for explicit continuation with \
if (withoutComments.endsWith('\\')) {
if (/^(?:[^"\\]|"[^"]*")*\\/.test(withoutComments)) {
// Special case: if this is a THEN \ line, don't continue
// Let the next line be processed as its own logical group
if (/\bTHEN\s*\\/.test(withoutComments)) {
return false;
}
return true;
}
@@ -58,6 +101,11 @@ export function activate(context: vscode.ExtensionContext) {
// Check if current line looks like a continuation of a condition
if (/^\s*(AND\b|OR\b|NOT\b|\()/i.test(withoutComments) && currentIndex > 0) {
// BUT: if this line contains THEN, it terminates the condition, so it should NOT continue
if (/\bTHEN\b/i.test(withoutComments)) {
return false;
}
// Look back to see if we're in a continuation chain
let prevIndex = currentIndex - 1;
while (prevIndex >= 0) {
@@ -66,7 +114,7 @@ export function activate(context: vscode.ExtensionContext) {
prevIndex--;
continue;
}
if (prevLine.endsWith('\\') ||
if (/\\.*$/.test(prevLine) ||
/^\s*IF\b/i.test(prevLine) ||
/^\s*(AND\b|OR\b|NOT\b|\()/i.test(prevLine)) {
return true;
@@ -117,16 +165,36 @@ export function activate(context: vscode.ExtensionContext) {
const fullLogical = logicalParts.join(' ').trim();
const firstLineTrimmed = removeComments(continuationLines[0]).trim();
// Check if this line follows a THEN \ continuation
const isPrevThenContinuation = i > 0 && /\bTHEN\s*\\/.test(removeComments(lines[i - 1]));
// Check if this is a THEN continuation that starts a new block
const isThenContinuation = /\bTHEN\s*\\/.test(removeComments(continuationLines[0]));
let thenBlockStarts = false;
if (isThenContinuation && continuationLines.length > 1) {
// Check if the continuation contains an opening construct
for (let k = 1; k < continuationLines.length; k++) {
const contLine = removeComments(continuationLines[k]).trim();
if (/^\s*(FUNCTION|DEF|SUB|WHILE|FOR|IF|BEGIN)\b/i.test(contLine)) {
thenBlockStarts = true;
break;
}
}
}
// Simple approach: look for keywords that affect indentation
const containsBegin = /\bBEGIN\b/i.test(fullLogical);
const containsNext = /\bNEXT\b/i.test(fullLogical);
const containsNext = /^\s*NEXT\b/i.test(firstLineTrimmed);
const isClosing = /^\s*(END FUNCTION|FEND|END SUB|ENDIF|WEND|NEXT)\b/i.test(firstLineTrimmed) ||
const isClosing = /^\s*(END\s+FUNCTION|FEND|END\s+SUB|ENDIF|WEND|NEXT)\b/i.test(firstLineTrimmed) ||
containsNext ||
(/^\s*ELSE\b/i.test(firstLineTrimmed) && !containsBegin);
const isOpening = /^\s*(FUNCTION|DEF|SUB|WHILE|FOR)\b/i.test(fullLogical) ||
containsBegin;
/^\s*(FUNCTION|DEF|SUB|WHILE|FOR)\b/i.test(firstLineTrimmed) ||
containsBegin ||
thenBlockStarts;
// Handle compound statements like ENDIF ELSE BEGIN
const isCompoundEndifElse = /\bENDIF\b.*\bELSE\s+BEGIN\b/i.test(fullLogical);
@@ -147,7 +215,25 @@ export function activate(context: vscode.ExtensionContext) {
if (trimmed === '') continue;
const indent = indentUnit.repeat(indentLevel);
// For continuation lines (k > 0), use appropriate indentation
let lineIndentLevel = indentLevel;
if (isPrevThenContinuation) {
// This line follows a THEN \ - it should be indented
lineIndentLevel = indentLevel + 1;
} else if (k > 0 && isThenContinuation) {
// This is a continuation after THEN - it should be indented
lineIndentLevel = indentLevel + 1;
} else if (k > 0) {
// Regular continuation line - check if the previous line ended with \
const prevLine = removeComments(continuationLines[k - 1]).trim();
if (prevLine.endsWith('\\')) {
// Keep the same indentation as the main statement
lineIndentLevel = indentLevel;
}
}
const indent = indentUnit.repeat(lineIndentLevel);
const formatted = indent + trimmed;
if (formatted !== original) {
@@ -218,6 +304,29 @@ export function activate(context: vscode.ExtensionContext) {
return false;
}
// Helper function to parse parameters from a parameter string
function parseParameters(paramString: string): string[] {
if (!paramString.trim()) {
return [];
}
const parameters: string[] = [];
const parts = paramString.split(',');
for (const part of parts) {
const trimmedPart = part.trim();
if (trimmedPart) {
// Extract parameter name using the identifier regex
const match = identifierRegex.exec(trimmedPart);
if (match) {
parameters.push(match[0]);
}
}
}
return parameters;
}
let i = 0;
while (i < lines.length) {
const originalLine = lines[i];
@@ -278,10 +387,24 @@ export function activate(context: vscode.ExtensionContext) {
const position = new vscode.Position(i, originalLine.indexOf(functionName));
const location = new vscode.Location(document.uri, position);
// Extract parameters
let parameters: string[] = [];
const afterName = afterKeyword.substring(nameMatch[0].length).trim();
// Check if there are parentheses after the function name
const parenMatch = /^\s*\(\s*(.*?)\s*\)/.exec(afterName);
if (parenMatch) {
// Extract parameter string and parse it
const paramString = parenMatch[1];
parameters = parseParameters(paramString);
}
definitions.push({
name: functionName,
location: location,
type: defType
type: defType,
parameters: parameters,
parameterCount: parameters.length
});
}
}
@@ -293,7 +416,84 @@ export function activate(context: vscode.ExtensionContext) {
return definitions;
}
// Definition provider - searches all files in workspace
// Function to build the initial symbol index
async function buildInitialSymbolIndex(): Promise<void> {
try {
console.log('Building symbol index...');
const workspaceFiles = await vscode.workspace.findFiles('**/*.{bas,BAS,j86,J86}');
for (const fileUri of workspaceFiles) {
try {
await updateIndexForFile(fileUri);
} catch (error) {
console.warn(`Failed to index file ${fileUri.toString()}:`, error);
}
}
console.log(`Symbol index built with ${symbolIndex.size} unique symbols across ${fileIndex.size} files`);
} catch (error) {
console.error('Failed to build initial symbol index:', error);
}
}
// Function to update the index for a specific file
async function updateIndexForFile(fileUri: vscode.Uri): Promise<void> {
try {
const document = await vscode.workspace.openTextDocument(fileUri);
const definitions = parseFunctionDefinitions(document);
const fileKey = fileUri.toString();
// Remove old definitions from the global symbol index
const oldDefinitions = fileIndex.get(fileKey) || [];
for (const oldDef of oldDefinitions) {
const symbolKey = oldDef.name.toLowerCase();
const symbolDefs = symbolIndex.get(symbolKey) || [];
const filteredDefs = symbolDefs.filter(def => def.location.uri.toString() !== fileKey);
if (filteredDefs.length === 0) {
symbolIndex.delete(symbolKey);
} else {
symbolIndex.set(symbolKey, filteredDefs);
}
}
// Add new definitions to the indexes
fileIndex.set(fileKey, definitions);
for (const definition of definitions) {
const symbolKey = definition.name.toLowerCase();
const existing = symbolIndex.get(symbolKey) || [];
existing.push(definition);
symbolIndex.set(symbolKey, existing);
}
} catch (error) {
console.warn(`Failed to update index for file ${fileUri.toString()}:`, error);
}
}
// Function to remove a file from the index
function removeFromIndex(fileUri: vscode.Uri): void {
const fileKey = fileUri.toString();
const definitions = fileIndex.get(fileKey) || [];
// Remove from symbol index
for (const definition of definitions) {
const symbolKey = definition.name.toLowerCase();
const symbolDefs = symbolIndex.get(symbolKey) || [];
const filteredDefs = symbolDefs.filter(def => def.location.uri.toString() !== fileKey);
if (filteredDefs.length === 0) {
symbolIndex.delete(symbolKey);
} else {
symbolIndex.set(symbolKey, filteredDefs);
}
}
// Remove from file index
fileIndex.delete(fileKey);
}
// Optimized definition provider - uses the symbol index
const definitionProvider: vscode.DefinitionProvider = {
async provideDefinition(
document: vscode.TextDocument,
@@ -305,57 +505,36 @@ export function activate(context: vscode.ExtensionContext) {
return undefined;
}
const word = document.getText(wordRange);
const word = document.getText(wordRange).toLowerCase();
const definitions = symbolIndex.get(word);
// Search in current document first
const currentDocDefs = parseFunctionDefinitions(document);
const localDef = currentDocDefs.find(def => def.name.toLowerCase() === word.toLowerCase());
if (!definitions || definitions.length === 0) {
return undefined;
}
// If multiple definitions exist, prefer the one in the current document
const localDef = definitions.find(def => def.location.uri.toString() === document.uri.toString());
if (localDef) {
return localDef.location;
}
// Search in all workspace files
const workspaceFiles = await vscode.workspace.findFiles('**/*.{bas,BAS,j86,J86}');
for (const fileUri of workspaceFiles) {
if (fileUri.toString() === document.uri.toString()) {
continue; // Skip current document as we already searched it
}
try {
const doc = await vscode.workspace.openTextDocument(fileUri);
const definitions = parseFunctionDefinitions(doc);
const definition = definitions.find(def => def.name.toLowerCase() === word.toLowerCase());
if (definition) {
return definition.location;
}
} catch (error) {
// Skip files that can't be opened
continue;
}
}
return undefined;
// Otherwise return the first definition found
return definitions[0].location;
}
};
// Workspace symbol provider - only searches open documents
// Updated workspace symbol provider - uses the symbol index
const workspaceSymbolProvider: vscode.WorkspaceSymbolProvider = {
async provideWorkspaceSymbols(
query: string,
token: vscode.CancellationToken
): Promise<vscode.SymbolInformation[]> {
const symbols: vscode.SymbolInformation[] = [];
const queryLower = query.toLowerCase();
// Only search in currently open documents
for (const document of vscode.workspace.textDocuments) {
// Only search in files with our language ID
if (document.languageId === '4690basic') {
const definitions = parseFunctionDefinitions(document);
for (const [symbolName, definitions] of symbolIndex) {
if (!query || symbolName.includes(queryLower)) {
for (const def of definitions) {
if (!query || def.name.toLowerCase().includes(query.toLowerCase())) {
const symbolKind = def.type === 'SUB' ? vscode.SymbolKind.Method : vscode.SymbolKind.Function;
const symbol = new vscode.SymbolInformation(
def.name,
@@ -367,40 +546,219 @@ export function activate(context: vscode.ExtensionContext) {
}
}
}
}
return symbols;
}
};
// Document symbol provider (for outline view)
// Enhanced Document Symbol Provider that provides hierarchical symbols for breadcrumbs
const documentSymbolProvider: vscode.DocumentSymbolProvider = {
provideDocumentSymbols(
document: vscode.TextDocument,
token: vscode.CancellationToken
): vscode.DocumentSymbol[] {
const definitions = parseFunctionDefinitions(document);
const symbols: vscode.DocumentSymbol[] = [];
return parseDocumentSymbolsWithRanges(document);
}
};
for (const def of definitions) {
const symbolKind = def.type === 'SUB' ? vscode.SymbolKind.Method : vscode.SymbolKind.Function;
const range = new vscode.Range(def.location.range.start, def.location.range.start);
// Function to parse document symbols with proper ranges for breadcrumbs
function parseDocumentSymbolsWithRanges(document: vscode.TextDocument): vscode.DocumentSymbol[] {
const symbols: vscode.DocumentSymbol[] = [];
const lines = document.getText().split('\n');
const identifierRegex = /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?/;
// Helper function to check if a keyword appears after a backslash or comment marker
function isKeywordCommentedOut(line: string, keywordMatch: RegExpExecArray): boolean {
const keywordStart = keywordMatch.index!;
let inQuotes = false;
for (let i = 0; i < keywordStart; i++) {
const char = line[i];
if (!inQuotes && char === '"') {
inQuotes = true;
} else if (inQuotes && char === '"') {
inQuotes = false;
} else if (!inQuotes && (char === '\\' || char === '!')) {
// Found a comment marker before the keyword, so keyword is commented out
return true;
}
}
return false;
}
// Helper function to parse parameters from a parameter string
function parseParameters(paramString: string): string[] {
if (!paramString.trim()) {
return [];
}
const parameters: string[] = [];
const parts = paramString.split(',');
for (const part of parts) {
const trimmedPart = part.trim();
if (trimmedPart) {
// Extract parameter name using the identifier regex
const match = identifierRegex.exec(trimmedPart);
if (match) {
parameters.push(match[0]);
}
}
}
return parameters;
}
// Find the end of a function/subroutine starting from a given line
function findFunctionEnd(startLine: number, functionType: 'FUNCTION' | 'DEF' | 'SUB'): number {
let endPattern: RegExp;
// console.log('functionType: ', functionType);
if (functionType === 'FUNCTION' || functionType === 'DEF') {
endPattern = /^\s*(END\s+FUNCTION)|(FEND)\b/i;
} else { // SUB
endPattern = /^\s*END\s+SUB\b/i;
}
// console.log('endPattern: ', endPattern);
for (let i = startLine + 1; i < lines.length; i++) {
const line = lines[i];
// Skip commented lines
if (/^\s*(!|\\)/.test(line)) {
continue;
}
// Skip empty lines
if (line.trim() === '') {
continue;
}
// console.log('Evaluating for end: ', line);
// Check if this line matches the end pattern
const endMatch = endPattern.exec(line);
if (endMatch) {
// console.log('Found match: ', endMatch);
// Check if the end keyword is not commented out
if (!isKeywordCommentedOut(line, endMatch)) {
// console.log('Returning i');
return i;
}
}
}
// If no end found, return the last line of the document
return lines.length - 1;
}
// Process each line looking for function/sub definitions
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmedLine = line.trim();
// Skip empty lines
if (trimmedLine === '') {
continue;
}
// Skip commented lines
if (/^\s*(!|\\)/.test(trimmedLine)) {
continue;
}
// Check if this line starts a function definition
const functionMatch = /^\s*(FUNCTION|DEF|SUB)\s+/i.exec(line);
if (!functionMatch) {
continue;
}
// Skip if the function keyword is commented out
if (isKeywordCommentedOut(line, functionMatch)) {
continue;
}
// console.log('Current line: ', trimmedLine);
// Extract the function type and name
const defType = functionMatch[1].toUpperCase() as 'FUNCTION' | 'SUB' | 'DEF';
// console.log('functionMatch: ', functionMatch);
// console.log('Definition type: ', defType);
// Find the identifier after the function keyword
const afterKeyword = line.substring(functionMatch[0].length).trim();
const nameMatch = identifierRegex.exec(afterKeyword);
// console.log('afterKeyword: ', afterKeyword);
// console.log('nameMatch: ', nameMatch);
if (!nameMatch) {
continue;
}
// Check if this is not an external declaration
if (/\bEXTERNAL\s*.*$/i.test(line)) {
continue;
}
const functionName = nameMatch[0];
const startPosition = new vscode.Position(i, line.indexOf(functionMatch[1]));
// Find the end of this function
const endLineIndex = findFunctionEnd(i, defType);
const endPosition = new vscode.Position(endLineIndex, lines[endLineIndex].length);
// Create the full range for the function
const fullRange = new vscode.Range(startPosition, endPosition);
// Create a selection range (just the function name)
const nameStartCol = line.indexOf(functionName);
const namePosition = new vscode.Position(i, nameStartCol);
const nameEndPosition = new vscode.Position(i, nameStartCol + functionName.length);
const selectionRange = new vscode.Range(namePosition, nameEndPosition);
// Extract parameters
let parameters: string[] = [];
const afterName = afterKeyword.substring(nameMatch[0].length).trim();
// Check if there are parentheses after the function name
const parenMatch = /^\s*\(\s*(.*?)\s*\)/.exec(afterName);
if (parenMatch) {
// Extract parameter string and parse it
const paramString = parenMatch[1];
parameters = parseParameters(paramString);
}
// Create symbol kind
const symbolKind = defType === 'SUB' ? vscode.SymbolKind.Method : vscode.SymbolKind.Function;
// Create detail string with parameters
let detail = defType;
if (parameters.length > 0) {
detail += `(${parameters.join(', ')})`;
}
const symbol = new vscode.DocumentSymbol(
def.name,
def.type,
functionName,
detail,
symbolKind,
range,
range
fullRange,
selectionRange
);
symbols.push(symbol);
}
return symbols;
}
};
// Hover provider to help VS Code recognize symbols and show proper highlighting
// Updated hover provider - now shows parameter information
const hoverProvider: vscode.HoverProvider = {
provideHover(
document: vscode.TextDocument,
@@ -412,41 +770,31 @@ export function activate(context: vscode.ExtensionContext) {
return undefined;
}
const word = document.getText(wordRange);
const word = document.getText(wordRange).toLowerCase();
const definitions = symbolIndex.get(word);
// Search for the function definition in current document
const currentDocDefs = parseFunctionDefinitions(document);
let definition = currentDocDefs.find(def => def.name.toLowerCase() === word.toLowerCase());
// If not found locally, search in open documents
if (!definition) {
for (const openDoc of vscode.workspace.textDocuments) {
if (openDoc.uri.toString() === document.uri.toString()) {
continue;
if (!definitions || definitions.length === 0) {
return undefined;
}
if (openDoc.languageId === '4690basic') {
const definitions = parseFunctionDefinitions(openDoc);
definition = definitions.find(def => def.name.toLowerCase() === word.toLowerCase());
// Prefer definition from current document, otherwise use first one
const definition = definitions.find(def => def.location.uri.toString() === document.uri.toString()) || definitions[0];
if (definition) {
break;
}
}
}
}
if (definition) {
const typeLabel = definition.type === 'SUB' ? 'Subroutine' : 'Function';
const content = new vscode.MarkdownString();
content.appendCodeblock(`${definition.type} ${definition.name}`, '4690basic');
content.appendText(`\n${typeLabel}: ${definition.name}`);
// Build function signature with parameters
let signature = `${definition.type} ${definition.name}`;
if (definition.parameterCount > 0) {
signature += `(${definition.parameters.join(', ')})`;
}
content.appendCodeblock(signature, '4690basic');
const path = vscode.workspace.name ? definition.location.uri.path.slice(definition.location.uri.path.indexOf(vscode.workspace.name)) : 'Archivo fuente no encontrado';
content.appendMarkdown(`***\n\`${path}\``);
return new vscode.Hover(content, wordRange);
}
return undefined;
}
};
// Register all providers
@@ -455,8 +803,14 @@ export function activate(context: vscode.ExtensionContext) {
vscode.languages.registerDefinitionProvider(selector, definitionProvider),
vscode.languages.registerWorkspaceSymbolProvider(workspaceSymbolProvider),
vscode.languages.registerDocumentSymbolProvider(selector, documentSymbolProvider),
vscode.languages.registerHoverProvider(selector, hoverProvider)
vscode.languages.registerHoverProvider(selector, hoverProvider),
saveWatcher,
fsWatcher
);
}
export function deactivate() {}
export function deactivate() {
// Clear the indexes on deactivation
symbolIndex.clear();
fileIndex.clear();
}

View File

@@ -3,23 +3,15 @@
"name": "4690 BASIC",
"patterns": [
{
"name": "string.quoted.double",
"begin": "\"",
"end": "\"",
"patterns": [
"include": "#comments"
},
{
"match": ".",
"name": "string.quoted.double"
}
]
"include": "#strings"
},
{
"name": "string.regexp",
"match": "\\b[\\w\\.]+:"
},
{
"include": "#comments"
},
{
"include": "#function-decl"
},
@@ -44,12 +36,6 @@
{
"include": "#math-function"
},
{
"include": "#string-function"
},
{
"include": "#variable-assignment"
},
{
"include": "#if-statement"
},
@@ -74,9 +60,6 @@
{
"include": "#keywords"
},
{
"include": "#strings"
},
{
"include": "#stmt-w-parameters"
},
@@ -98,11 +81,27 @@
{
"include": "#number-literal"
},
{
"include": "#string-function"
},
{
"include": "#identifier"
}
],
"repository": {
"comments": {
"patterns": [
{
"name": "comment.line",
"match": "!.*|\\bREM.*|\\bREMARK.*"
}
]
},
"strings": {
"name": "string.quoted.double",
"begin": "\"",
"end": "\""
},
"line-continuation": {
"name": "meta.line.continuation",
"match": "\\\\(.*)\\n",
@@ -112,14 +111,6 @@
}
}
},
"comments": {
"patterns": [
{
"name": "comment.line",
"match": "!.*|REM.*|REMARK.*"
}
]
},
"function-decl": {
"name": "meta.function",
"begin": "(?i)\\b(FUNCTION|DEF)\\s+((\\?|[a-zA-Z])([a-zA-Z0-9#.]*)[\\$#%]?)",
@@ -184,7 +175,7 @@
},
"subroutine-end": {
"name": "keyword.control",
"match": "END SUB|end sub"
"match": "(?i)END\\s+SUB"
},
"integer-decl": {
"begin": "(INTEGER|integer)((\\*)(1|2|4))?",
@@ -206,7 +197,7 @@
},
{
"name": "entity.name.function",
"match": "(\\?|[a-zA-Z])([a-zA-Z0-9#.]*)([\\$#%])?",
"match": "(\\?|[a-zA-Z])([a-zA-Z0-9#\\.]*)([\\$#%])?",
"captures": {
"3": {
"name": "keyword.operator"
@@ -283,7 +274,7 @@
}
},
"real-decl": {
"begin": "REAL|real",
"begin": "(?i)REAL\\s+",
"beginCaptures": {
"0": {
"name": "storage.type"
@@ -365,25 +356,7 @@
},
"math-function": {
"name": "support.function.arithmetic",
"match": "(?i)\\b(ABS|CHR\\$|FLOAT|INT%?|MOD|PEEK|SGN|SHIFT|STR\\$|TAB)\\b[^%\\$\\.]"
},
"variable-assignment": {
"name": "meta.assignment",
"match": "^\\s*(\\?|[a-zA-Z])([a-zA-Z0-9#.]*)([\\$#%])?\\s+(=)",
"captures": {
"1": {
"name": "variable.name"
},
"2": {
"name": "variable.name"
},
"3": {
"name": "keyword.operator"
},
"4": {
"name": "keyword.operator.arithmetic"
}
}
"match": "(?i)\\b(ABS|CHR\\$|FLOAT|INT%?|MOD|PEEK|SGN|SHIFT|STR\\$|TAB)(?=\\s*\\()"
},
"numeric-expression": {
"name": "meta.expression.numeric",
@@ -405,24 +378,28 @@
]
},
"identifier": {
"patterns": [
{
"name": "variable.name",
"match": "\\b(\\?|[a-zA-Z])([a-zA-Z0-9#.]*)[\\$#%]?\\b"
"name": "meta.identifier",
"match": "((\\?|[a-zA-Z])([a-zA-Z0-9#\\.]*))([\\$#%])?",
"captures": {
"1": {
"name": "variable.name"
},
"4": {
"name": "keyword.operator"
}
}
]
},
"number-literal": {
"patterns": [
{
"name": "constant.numeric",
"match": "(?i)\\b([+-]?[0-9.]+)(E[+-]?[0-9]+)?[hb]?\\b"
"match": "(?i)\\b([+-]?[0-9\\.ABCDEF]+)(E[+-]?[0-9]+)?[HB]?\\b"
}
]
},
"arithmetic-operator": {
"name": "keyword.operator.arithmetic",
"match": "[\\+-\\/\\^\\*]"
"match": "[\\+-\\/\\^\\*#]"
},
"relational-operator": {
"name": "keyword.operator.relational",
@@ -430,27 +407,17 @@
},
"logical-operator": {
"name": "keyword.operator.logical",
"match": "\\b(NOT|AND|OR|XOR)\\b"
"match": "(?i)\\b(NOT|AND|OR|XOR)\\b"
},
"string-operator": {
"name": "keyword.operator",
"match": "[+]"
},
"string-function": {
"begin": "(?i)\\b(ASC|LEN|PACK\\$|TRANSLATE\\$|UCASE\\$|UNPACK\\$|VAL|MID\\$|LEFT\\$|RIGHT\\$|MATCH)\\b",
"beginCaptures": {
"1": {
"name": "support.function.string"
},
"0": {
"name": "meta.function-call.begin"
}
},
"end": "\\(",
"endCaptures": {
"0": {
"name": "punctuation.parameters.start"
}
"name": "support.function.builtin",
"match": "(?i)\\b(ASC|LEN|PACK\\$|TRANSLATE\\$|UCASE\\$|UNPACK\\$|VAL|MID\\$|LEFT\\$|RIGHT\\$|MATCH|SUBSTR|STRING\\$)(?=\\s*\\()",
"captures": {
"1": { "name": "support.function.builtin"}
}
},
"string-expression": {
@@ -542,7 +509,7 @@
"patterns": [
{
"name": "keyword.control",
"match": "(?i)\\b(GOSUB|RETURN|GOTO|WHILE|WEND|NEXT|ON|ERROR|STOP|RANDOMIZE|CHAIN|COMMON|CALL|EXIT SUB|FORM)\\b[^%\\$\\.]"
"match": "(?i)\\b(GOSUB|RETURN|GOTO|WHILE|WEND|NEXT|ON|ERROR|STOP|RANDOMIZE|CHAIN|COMMON|CALL|EXIT\\s+SUB|EXIT\\s+FUNCTION|FORM)\\b[^%\\$\\.]"
},
{
"name": "support.function",
@@ -550,17 +517,6 @@
}
]
},
"strings": {
"name": "string.quoted.double.4690basic",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.4690basic",
"match": "\\\\."
}
]
},
"single-stmts": {
"name": "support.function",
"match": "(COMMAND$|CONCHAR%|CONSOLE|DATE$)"
@@ -585,24 +541,16 @@
{
"include": "#identifier"
},
{
"name": "punctuation.separator.parameter",
"match": ","
},
{
"name": "punctuation.separator.option",
"match": ";"
},
{
"match": "\\s+",
"name": "text.whitespace"
}
],
"end": "$"
"end": "(?<!\\\\)($|\\n)"
},
"open-close": {
"name": "support.function",
"match": "(?i)OPEN|CLOSE|KEYED|RECL|AS"
"match": "(?i)\\b(OPEN|CLOSE|KEYED|RECL|AS[^\\$])\\b"
},
"access-stmt": {
"match": "(ACCESS)\\s+((NOREAD|NOWRITE|NODEL)(\\s*,\\s*(NOREAD|NOWRITE|NODEL)){0,2})",
@@ -702,9 +650,6 @@
{
"include": "#number-literal"
},
{
"include": "#number-literal"
},
{
"include": "#arithmetic-operator"
},
@@ -732,6 +677,9 @@
"name": "keyword.control",
"match": "(?i)(BEGIN)"
},
{
"include": "#identifier"
},
{
"include": "#line-continuation"
},
@@ -743,7 +691,7 @@
"name": "text.whitespace"
}
],
"end": "(?<!\\\\)\\n"
"end": "(?<!\\\\)(\\n)"
},
"if-statement-end": {
"name": "meta.if.end",
@@ -811,7 +759,9 @@
"name": "meta.for",
"begin": "(?i)\\bFOR\\s+",
"beginCaptures": {
"0": { "name": "keyword.control" }
"0": {
"name": "keyword.control"
}
},
"patterns": [
{
@@ -832,6 +782,9 @@
},
{
"include": "#number-literal"
},
{
"include": "#comments"
}
],
"end": "(?<!\\\\)\\n"

View File

@@ -10,7 +10,7 @@ const path = require('path');
/** @type WebpackConfig */
const extensionConfig = {
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
mode: 'production', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {