before rewriting formatter
This commit is contained in:
68
README.md
68
README.md
@@ -1,71 +1,9 @@
|
|||||||
# format-4690 README
|
# format-4690 README
|
||||||
|
|
||||||
This is the README for your extension "format-4690". After writing up a brief description, we recommend including the following sections.
|
Hola
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Following extension guidelines
|
### 2025.06.01
|
||||||
|
|
||||||
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
|
Inicial
|
||||||
|
|
||||||
* [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!**
|
|
||||||
|
|||||||
@@ -23,5 +23,6 @@
|
|||||||
["(", ")"],
|
["(", ")"],
|
||||||
["\"", "\""],
|
["\"", "\""],
|
||||||
["'", "'"]
|
["'", "'"]
|
||||||
]
|
],
|
||||||
|
"wordPattern": "(\\?|[a-zA-Z])([a-zA-Z0-9#\\.]*)[$#%]?"
|
||||||
}
|
}
|
||||||
612
src/extension.ts
612
src/extension.ts
@@ -4,12 +4,42 @@ interface FunctionDefinition {
|
|||||||
name: string;
|
name: string;
|
||||||
location: vscode.Location;
|
location: vscode.Location;
|
||||||
type: 'FUNCTION' | 'SUB' | 'DEF';
|
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' };
|
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 = {
|
const formatterProvider: vscode.DocumentFormattingEditProvider = {
|
||||||
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] {
|
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] {
|
||||||
const config = vscode.workspace.getConfiguration('4690basic.format');
|
const config = vscode.workspace.getConfiguration('4690basic.format');
|
||||||
@@ -26,7 +56,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
function removeComments(line: string): string {
|
function removeComments(line: string): string {
|
||||||
let inQuotes = false;
|
let inQuotes = false;
|
||||||
let quoteChar = '';
|
let quoteChar = '';
|
||||||
|
|
||||||
for (let i = 0; i < line.length; i++) {
|
for (let i = 0; i < line.length; i++) {
|
||||||
const char = line[i];
|
const char = line[i];
|
||||||
if (!inQuotes && (char === '"' || char === "'")) {
|
if (!inQuotes && (char === '"' || char === "'")) {
|
||||||
@@ -44,20 +74,38 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
// Helper function to check if a line should continue
|
// Helper function to check if a line should continue
|
||||||
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
|
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();
|
const withoutComments = removeComments(line).trim();
|
||||||
|
|
||||||
// Check for explicit continuation with \
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for IF without THEN on the same line
|
// Check for IF without THEN on the same line
|
||||||
if (/^\s*IF\b/i.test(withoutComments) && !/\bTHEN\b/i.test(withoutComments)) {
|
if (/^\s*IF\b/i.test(withoutComments) && !/\bTHEN\b/i.test(withoutComments)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if current line looks like a continuation of a condition
|
// Check if current line looks like a continuation of a condition
|
||||||
if (/^\s*(AND\b|OR\b|NOT\b|\()/i.test(withoutComments) && currentIndex > 0) {
|
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
|
// Look back to see if we're in a continuation chain
|
||||||
let prevIndex = currentIndex - 1;
|
let prevIndex = currentIndex - 1;
|
||||||
while (prevIndex >= 0) {
|
while (prevIndex >= 0) {
|
||||||
@@ -66,22 +114,22 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
prevIndex--;
|
prevIndex--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (prevLine.endsWith('\\') ||
|
if (/\\.*$/.test(prevLine) ||
|
||||||
/^\s*IF\b/i.test(prevLine) ||
|
/^\s*IF\b/i.test(prevLine) ||
|
||||||
/^\s*(AND\b|OR\b|NOT\b|\()/i.test(prevLine)) {
|
/^\s*(AND\b|OR\b|NOT\b|\()/i.test(prevLine)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (i < lines.length) {
|
while (i < lines.length) {
|
||||||
const originalLine = lines[i];
|
const originalLine = lines[i];
|
||||||
const trimmedLine = originalLine.trim();
|
const trimmedLine = originalLine.trim();
|
||||||
|
|
||||||
// Skip empty lines
|
// Skip empty lines
|
||||||
if (trimmedLine === '') {
|
if (trimmedLine === '') {
|
||||||
i++;
|
i++;
|
||||||
@@ -91,7 +139,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
// Collect continuation lines
|
// Collect continuation lines
|
||||||
let continuationLines = [originalLine];
|
let continuationLines = [originalLine];
|
||||||
let j = i;
|
let j = i;
|
||||||
|
|
||||||
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
||||||
if (j + 1 < lines.length) {
|
if (j + 1 < lines.length) {
|
||||||
j++;
|
j++;
|
||||||
@@ -113,20 +161,40 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
logicalParts.push(part);
|
logicalParts.push(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullLogical = logicalParts.join(' ').trim();
|
const fullLogical = logicalParts.join(' ').trim();
|
||||||
const firstLineTrimmed = removeComments(continuationLines[0]).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
|
// Simple approach: look for keywords that affect indentation
|
||||||
const containsBegin = /\bBEGIN\b/i.test(fullLogical);
|
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 ||
|
containsNext ||
|
||||||
(/^\s*ELSE\b/i.test(firstLineTrimmed) && !containsBegin);
|
(/^\s*ELSE\b/i.test(firstLineTrimmed) && !containsBegin);
|
||||||
|
|
||||||
const isOpening = /^\s*(FUNCTION|DEF|SUB|WHILE|FOR)\b/i.test(fullLogical) ||
|
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
|
// Handle compound statements like ENDIF ELSE BEGIN
|
||||||
const isCompoundEndifElse = /\bENDIF\b.*\bELSE\s+BEGIN\b/i.test(fullLogical);
|
const isCompoundEndifElse = /\bENDIF\b.*\bELSE\s+BEGIN\b/i.test(fullLogical);
|
||||||
@@ -147,7 +215,25 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
if (trimmed === '') continue;
|
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;
|
const formatted = indent + trimmed;
|
||||||
|
|
||||||
if (formatted !== original) {
|
if (formatted !== original) {
|
||||||
@@ -181,7 +267,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
function removeComments(line: string): string {
|
function removeComments(line: string): string {
|
||||||
let inQuotes = false;
|
let inQuotes = false;
|
||||||
let quoteChar = '';
|
let quoteChar = '';
|
||||||
|
|
||||||
for (let i = 0; i < line.length; i++) {
|
for (let i = 0; i < line.length; i++) {
|
||||||
const char = line[i];
|
const char = line[i];
|
||||||
if (!inQuotes && (char === '"' || char === "'")) {
|
if (!inQuotes && (char === '"' || char === "'")) {
|
||||||
@@ -200,12 +286,12 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
// Helper function to check if a line should continue
|
// Helper function to check if a line should continue
|
||||||
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
|
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
|
||||||
const withoutComments = removeComments(line).trim();
|
const withoutComments = removeComments(line).trim();
|
||||||
|
|
||||||
// Check for explicit continuation with \
|
// Check for explicit continuation with \
|
||||||
if (withoutComments.endsWith('\\')) {
|
if (withoutComments.endsWith('\\')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For function definitions, also check if we have incomplete parentheses
|
// For function definitions, also check if we have incomplete parentheses
|
||||||
if (/^\s*(FUNCTION|DEF|SUB)\b/i.test(withoutComments)) {
|
if (/^\s*(FUNCTION|DEF|SUB)\b/i.test(withoutComments)) {
|
||||||
const openParens = (withoutComments.match(/\(/g) || []).length;
|
const openParens = (withoutComments.match(/\(/g) || []).length;
|
||||||
@@ -214,15 +300,38 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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;
|
let i = 0;
|
||||||
while (i < lines.length) {
|
while (i < lines.length) {
|
||||||
const originalLine = lines[i];
|
const originalLine = lines[i];
|
||||||
const trimmedLine = originalLine.trim();
|
const trimmedLine = originalLine.trim();
|
||||||
|
|
||||||
// Skip empty lines
|
// Skip empty lines
|
||||||
if (trimmedLine === '') {
|
if (trimmedLine === '') {
|
||||||
i++;
|
i++;
|
||||||
@@ -239,7 +348,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
// Collect continuation lines for this function definition
|
// Collect continuation lines for this function definition
|
||||||
let continuationLines = [originalLine];
|
let continuationLines = [originalLine];
|
||||||
let j = i;
|
let j = i;
|
||||||
|
|
||||||
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
||||||
if (j + 1 < lines.length) {
|
if (j + 1 < lines.length) {
|
||||||
j++;
|
j++;
|
||||||
@@ -261,27 +370,41 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
logicalParts.push(part);
|
logicalParts.push(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullLogical = logicalParts.join(' ').trim();
|
const fullLogical = logicalParts.join(' ').trim();
|
||||||
|
|
||||||
// Check if this is not an external declaration
|
// Check if this is not an external declaration
|
||||||
if (!/\bEXTERNAL\s*$/i.test(fullLogical)) {
|
if (!/\bEXTERNAL\s*$/i.test(fullLogical)) {
|
||||||
// Extract the function type and name
|
// Extract the function type and name
|
||||||
const defType = functionMatch[1].toUpperCase() as 'FUNCTION' | 'SUB' | 'DEF';
|
const defType = functionMatch[1].toUpperCase() as 'FUNCTION' | 'SUB' | 'DEF';
|
||||||
|
|
||||||
// Find the identifier after the function keyword
|
// Find the identifier after the function keyword
|
||||||
const afterKeyword = fullLogical.substring(functionMatch[0].length).trim();
|
const afterKeyword = fullLogical.substring(functionMatch[0].length).trim();
|
||||||
const nameMatch = identifierRegex.exec(afterKeyword);
|
const nameMatch = identifierRegex.exec(afterKeyword);
|
||||||
|
|
||||||
if (nameMatch) {
|
if (nameMatch) {
|
||||||
const functionName = nameMatch[0];
|
const functionName = nameMatch[0];
|
||||||
const position = new vscode.Position(i, originalLine.indexOf(functionName));
|
const position = new vscode.Position(i, originalLine.indexOf(functionName));
|
||||||
const location = new vscode.Location(document.uri, position);
|
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({
|
definitions.push({
|
||||||
name: functionName,
|
name: functionName,
|
||||||
location: location,
|
location: location,
|
||||||
type: defType
|
type: defType,
|
||||||
|
parameters: parameters,
|
||||||
|
parameterCount: parameters.length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +416,84 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
return definitions;
|
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 = {
|
const definitionProvider: vscode.DefinitionProvider = {
|
||||||
async provideDefinition(
|
async provideDefinition(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
@@ -305,66 +505,44 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
return undefined;
|
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);
|
if (!definitions || definitions.length === 0) {
|
||||||
const localDef = currentDocDefs.find(def => def.name.toLowerCase() === word.toLowerCase());
|
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) {
|
if (localDef) {
|
||||||
return localDef.location;
|
return localDef.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search in all workspace files
|
// Otherwise return the first definition found
|
||||||
const workspaceFiles = await vscode.workspace.findFiles('**/*.{bas,BAS,j86,J86}');
|
return definitions[0].location;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Workspace symbol provider - only searches open documents
|
// Updated workspace symbol provider - uses the symbol index
|
||||||
const workspaceSymbolProvider: vscode.WorkspaceSymbolProvider = {
|
const workspaceSymbolProvider: vscode.WorkspaceSymbolProvider = {
|
||||||
async provideWorkspaceSymbols(
|
async provideWorkspaceSymbols(
|
||||||
query: string,
|
query: string,
|
||||||
token: vscode.CancellationToken
|
token: vscode.CancellationToken
|
||||||
): Promise<vscode.SymbolInformation[]> {
|
): Promise<vscode.SymbolInformation[]> {
|
||||||
const symbols: vscode.SymbolInformation[] = [];
|
const symbols: vscode.SymbolInformation[] = [];
|
||||||
|
const queryLower = query.toLowerCase();
|
||||||
// Only search in currently open documents
|
|
||||||
for (const document of vscode.workspace.textDocuments) {
|
for (const [symbolName, definitions] of symbolIndex) {
|
||||||
// Only search in files with our language ID
|
if (!query || symbolName.includes(queryLower)) {
|
||||||
if (document.languageId === '4690basic') {
|
|
||||||
const definitions = parseFunctionDefinitions(document);
|
|
||||||
|
|
||||||
for (const def of definitions) {
|
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 symbolKind = def.type === 'SUB' ? vscode.SymbolKind.Method : vscode.SymbolKind.Function;
|
const symbol = new vscode.SymbolInformation(
|
||||||
const symbol = new vscode.SymbolInformation(
|
def.name,
|
||||||
def.name,
|
symbolKind,
|
||||||
symbolKind,
|
'',
|
||||||
'',
|
def.location
|
||||||
def.location
|
);
|
||||||
);
|
symbols.push(symbol);
|
||||||
symbols.push(symbol);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,34 +551,214 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Document symbol provider (for outline view)
|
// Enhanced Document Symbol Provider that provides hierarchical symbols for breadcrumbs
|
||||||
const documentSymbolProvider: vscode.DocumentSymbolProvider = {
|
const documentSymbolProvider: vscode.DocumentSymbolProvider = {
|
||||||
provideDocumentSymbols(
|
provideDocumentSymbols(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
token: vscode.CancellationToken
|
token: vscode.CancellationToken
|
||||||
): vscode.DocumentSymbol[] {
|
): vscode.DocumentSymbol[] {
|
||||||
const definitions = parseFunctionDefinitions(document);
|
return parseDocumentSymbolsWithRanges(document);
|
||||||
const symbols: vscode.DocumentSymbol[] = [];
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
const symbol = new vscode.DocumentSymbol(
|
|
||||||
def.name,
|
|
||||||
def.type,
|
|
||||||
symbolKind,
|
|
||||||
range,
|
|
||||||
range
|
|
||||||
);
|
|
||||||
symbols.push(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
return symbols;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hover provider to help VS Code recognize symbols and show proper highlighting
|
// 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(
|
||||||
|
functionName,
|
||||||
|
detail,
|
||||||
|
symbolKind,
|
||||||
|
fullRange,
|
||||||
|
selectionRange
|
||||||
|
);
|
||||||
|
|
||||||
|
symbols.push(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updated hover provider - now shows parameter information
|
||||||
const hoverProvider: vscode.HoverProvider = {
|
const hoverProvider: vscode.HoverProvider = {
|
||||||
provideHover(
|
provideHover(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
@@ -412,40 +770,30 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
return undefined;
|
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);
|
if (!definitions || definitions.length === 0) {
|
||||||
let definition = currentDocDefs.find(def => def.name.toLowerCase() === word.toLowerCase());
|
return undefined;
|
||||||
|
|
||||||
// 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 (openDoc.languageId === '4690basic') {
|
|
||||||
const definitions = parseFunctionDefinitions(openDoc);
|
|
||||||
definition = definitions.find(def => def.name.toLowerCase() === word.toLowerCase());
|
|
||||||
|
|
||||||
if (definition) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (definition) {
|
// Prefer definition from current document, otherwise use first one
|
||||||
const typeLabel = definition.type === 'SUB' ? 'Subroutine' : 'Function';
|
const definition = definitions.find(def => def.location.uri.toString() === document.uri.toString()) || definitions[0];
|
||||||
const content = new vscode.MarkdownString();
|
|
||||||
content.appendCodeblock(`${definition.type} ${definition.name}`, '4690basic');
|
const content = new vscode.MarkdownString();
|
||||||
content.appendText(`\n${typeLabel}: ${definition.name}`);
|
|
||||||
|
// Build function signature with parameters
|
||||||
return new vscode.Hover(content, wordRange);
|
let signature = `${definition.type} ${definition.name}`;
|
||||||
|
if (definition.parameterCount > 0) {
|
||||||
|
signature += `(${definition.parameters.join(', ')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -455,8 +803,14 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
vscode.languages.registerDefinitionProvider(selector, definitionProvider),
|
vscode.languages.registerDefinitionProvider(selector, definitionProvider),
|
||||||
vscode.languages.registerWorkspaceSymbolProvider(workspaceSymbolProvider),
|
vscode.languages.registerWorkspaceSymbolProvider(workspaceSymbolProvider),
|
||||||
vscode.languages.registerDocumentSymbolProvider(selector, documentSymbolProvider),
|
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();
|
||||||
|
}
|
||||||
@@ -3,23 +3,15 @@
|
|||||||
"name": "4690 BASIC",
|
"name": "4690 BASIC",
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"name": "string.quoted.double",
|
"include": "#comments"
|
||||||
"begin": "\"",
|
},
|
||||||
"end": "\"",
|
{
|
||||||
"patterns": [
|
"include": "#strings"
|
||||||
{
|
|
||||||
"match": ".",
|
|
||||||
"name": "string.quoted.double"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "string.regexp",
|
"name": "string.regexp",
|
||||||
"match": "\\b[\\w\\.]+:"
|
"match": "\\b[\\w\\.]+:"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"include": "#comments"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"include": "#function-decl"
|
"include": "#function-decl"
|
||||||
},
|
},
|
||||||
@@ -44,12 +36,6 @@
|
|||||||
{
|
{
|
||||||
"include": "#math-function"
|
"include": "#math-function"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"include": "#string-function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#variable-assignment"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"include": "#if-statement"
|
"include": "#if-statement"
|
||||||
},
|
},
|
||||||
@@ -74,9 +60,6 @@
|
|||||||
{
|
{
|
||||||
"include": "#keywords"
|
"include": "#keywords"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"include": "#strings"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"include": "#stmt-w-parameters"
|
"include": "#stmt-w-parameters"
|
||||||
},
|
},
|
||||||
@@ -98,11 +81,27 @@
|
|||||||
{
|
{
|
||||||
"include": "#number-literal"
|
"include": "#number-literal"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"include": "#string-function"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"include": "#identifier"
|
"include": "#identifier"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
|
"comments": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "comment.line",
|
||||||
|
"match": "!.*|\\bREM.*|\\bREMARK.*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"strings": {
|
||||||
|
"name": "string.quoted.double",
|
||||||
|
"begin": "\"",
|
||||||
|
"end": "\""
|
||||||
|
},
|
||||||
"line-continuation": {
|
"line-continuation": {
|
||||||
"name": "meta.line.continuation",
|
"name": "meta.line.continuation",
|
||||||
"match": "\\\\(.*)\\n",
|
"match": "\\\\(.*)\\n",
|
||||||
@@ -112,14 +111,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"comments": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "comment.line",
|
|
||||||
"match": "!.*|REM.*|REMARK.*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"function-decl": {
|
"function-decl": {
|
||||||
"name": "meta.function",
|
"name": "meta.function",
|
||||||
"begin": "(?i)\\b(FUNCTION|DEF)\\s+((\\?|[a-zA-Z])([a-zA-Z0-9#.]*)[\\$#%]?)",
|
"begin": "(?i)\\b(FUNCTION|DEF)\\s+((\\?|[a-zA-Z])([a-zA-Z0-9#.]*)[\\$#%]?)",
|
||||||
@@ -184,7 +175,7 @@
|
|||||||
},
|
},
|
||||||
"subroutine-end": {
|
"subroutine-end": {
|
||||||
"name": "keyword.control",
|
"name": "keyword.control",
|
||||||
"match": "END SUB|end sub"
|
"match": "(?i)END\\s+SUB"
|
||||||
},
|
},
|
||||||
"integer-decl": {
|
"integer-decl": {
|
||||||
"begin": "(INTEGER|integer)((\\*)(1|2|4))?",
|
"begin": "(INTEGER|integer)((\\*)(1|2|4))?",
|
||||||
@@ -206,7 +197,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "entity.name.function",
|
"name": "entity.name.function",
|
||||||
"match": "(\\?|[a-zA-Z])([a-zA-Z0-9#.]*)([\\$#%])?",
|
"match": "(\\?|[a-zA-Z])([a-zA-Z0-9#\\.]*)([\\$#%])?",
|
||||||
"captures": {
|
"captures": {
|
||||||
"3": {
|
"3": {
|
||||||
"name": "keyword.operator"
|
"name": "keyword.operator"
|
||||||
@@ -283,7 +274,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"real-decl": {
|
"real-decl": {
|
||||||
"begin": "REAL|real",
|
"begin": "(?i)REAL\\s+",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": {
|
"0": {
|
||||||
"name": "storage.type"
|
"name": "storage.type"
|
||||||
@@ -365,25 +356,7 @@
|
|||||||
},
|
},
|
||||||
"math-function": {
|
"math-function": {
|
||||||
"name": "support.function.arithmetic",
|
"name": "support.function.arithmetic",
|
||||||
"match": "(?i)\\b(ABS|CHR\\$|FLOAT|INT%?|MOD|PEEK|SGN|SHIFT|STR\\$|TAB)\\b[^%\\$\\.]"
|
"match": "(?i)\\b(ABS|CHR\\$|FLOAT|INT%?|MOD|PEEK|SGN|SHIFT|STR\\$|TAB)(?=\\s*\\()"
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"numeric-expression": {
|
"numeric-expression": {
|
||||||
"name": "meta.expression.numeric",
|
"name": "meta.expression.numeric",
|
||||||
@@ -405,24 +378,28 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"patterns": [
|
"name": "meta.identifier",
|
||||||
{
|
"match": "((\\?|[a-zA-Z])([a-zA-Z0-9#\\.]*))([\\$#%])?",
|
||||||
"name": "variable.name",
|
"captures": {
|
||||||
"match": "\\b(\\?|[a-zA-Z])([a-zA-Z0-9#.]*)[\\$#%]?\\b"
|
"1": {
|
||||||
|
"name": "variable.name"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"name": "keyword.operator"
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
},
|
},
|
||||||
"number-literal": {
|
"number-literal": {
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"name": "constant.numeric",
|
"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": {
|
"arithmetic-operator": {
|
||||||
"name": "keyword.operator.arithmetic",
|
"name": "keyword.operator.arithmetic",
|
||||||
"match": "[\\+-\\/\\^\\*]"
|
"match": "[\\+-\\/\\^\\*#]"
|
||||||
},
|
},
|
||||||
"relational-operator": {
|
"relational-operator": {
|
||||||
"name": "keyword.operator.relational",
|
"name": "keyword.operator.relational",
|
||||||
@@ -430,27 +407,17 @@
|
|||||||
},
|
},
|
||||||
"logical-operator": {
|
"logical-operator": {
|
||||||
"name": "keyword.operator.logical",
|
"name": "keyword.operator.logical",
|
||||||
"match": "\\b(NOT|AND|OR|XOR)\\b"
|
"match": "(?i)\\b(NOT|AND|OR|XOR)\\b"
|
||||||
},
|
},
|
||||||
"string-operator": {
|
"string-operator": {
|
||||||
"name": "keyword.operator",
|
"name": "keyword.operator",
|
||||||
"match": "[+]"
|
"match": "[+]"
|
||||||
},
|
},
|
||||||
"string-function": {
|
"string-function": {
|
||||||
"begin": "(?i)\\b(ASC|LEN|PACK\\$|TRANSLATE\\$|UCASE\\$|UNPACK\\$|VAL|MID\\$|LEFT\\$|RIGHT\\$|MATCH)\\b",
|
"name": "support.function.builtin",
|
||||||
"beginCaptures": {
|
"match": "(?i)\\b(ASC|LEN|PACK\\$|TRANSLATE\\$|UCASE\\$|UNPACK\\$|VAL|MID\\$|LEFT\\$|RIGHT\\$|MATCH|SUBSTR|STRING\\$)(?=\\s*\\()",
|
||||||
"1": {
|
"captures": {
|
||||||
"name": "support.function.string"
|
"1": { "name": "support.function.builtin"}
|
||||||
},
|
|
||||||
"0": {
|
|
||||||
"name": "meta.function-call.begin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"end": "\\(",
|
|
||||||
"endCaptures": {
|
|
||||||
"0": {
|
|
||||||
"name": "punctuation.parameters.start"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string-expression": {
|
"string-expression": {
|
||||||
@@ -542,7 +509,7 @@
|
|||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"name": "keyword.control",
|
"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",
|
"name": "support.function",
|
||||||
@@ -550,17 +517,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"strings": {
|
|
||||||
"name": "string.quoted.double.4690basic",
|
|
||||||
"begin": "\"",
|
|
||||||
"end": "\"",
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "constant.character.escape.4690basic",
|
|
||||||
"match": "\\\\."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"single-stmts": {
|
"single-stmts": {
|
||||||
"name": "support.function",
|
"name": "support.function",
|
||||||
"match": "(COMMAND$|CONCHAR%|CONSOLE|DATE$)"
|
"match": "(COMMAND$|CONCHAR%|CONSOLE|DATE$)"
|
||||||
@@ -585,24 +541,16 @@
|
|||||||
{
|
{
|
||||||
"include": "#identifier"
|
"include": "#identifier"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "punctuation.separator.parameter",
|
|
||||||
"match": ","
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "punctuation.separator.option",
|
|
||||||
"match": ";"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"match": "\\s+",
|
"match": "\\s+",
|
||||||
"name": "text.whitespace"
|
"name": "text.whitespace"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"end": "$"
|
"end": "(?<!\\\\)($|\\n)"
|
||||||
},
|
},
|
||||||
"open-close": {
|
"open-close": {
|
||||||
"name": "support.function",
|
"name": "support.function",
|
||||||
"match": "(?i)OPEN|CLOSE|KEYED|RECL|AS"
|
"match": "(?i)\\b(OPEN|CLOSE|KEYED|RECL|AS[^\\$])\\b"
|
||||||
},
|
},
|
||||||
"access-stmt": {
|
"access-stmt": {
|
||||||
"match": "(ACCESS)\\s+((NOREAD|NOWRITE|NODEL)(\\s*,\\s*(NOREAD|NOWRITE|NODEL)){0,2})",
|
"match": "(ACCESS)\\s+((NOREAD|NOWRITE|NODEL)(\\s*,\\s*(NOREAD|NOWRITE|NODEL)){0,2})",
|
||||||
@@ -702,9 +650,6 @@
|
|||||||
{
|
{
|
||||||
"include": "#number-literal"
|
"include": "#number-literal"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"include": "#number-literal"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"include": "#arithmetic-operator"
|
"include": "#arithmetic-operator"
|
||||||
},
|
},
|
||||||
@@ -732,6 +677,9 @@
|
|||||||
"name": "keyword.control",
|
"name": "keyword.control",
|
||||||
"match": "(?i)(BEGIN)"
|
"match": "(?i)(BEGIN)"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"include": "#identifier"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"include": "#line-continuation"
|
"include": "#line-continuation"
|
||||||
},
|
},
|
||||||
@@ -743,7 +691,7 @@
|
|||||||
"name": "text.whitespace"
|
"name": "text.whitespace"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"end": "(?<!\\\\)\\n"
|
"end": "(?<!\\\\)(\\n)"
|
||||||
},
|
},
|
||||||
"if-statement-end": {
|
"if-statement-end": {
|
||||||
"name": "meta.if.end",
|
"name": "meta.if.end",
|
||||||
@@ -811,7 +759,9 @@
|
|||||||
"name": "meta.for",
|
"name": "meta.for",
|
||||||
"begin": "(?i)\\bFOR\\s+",
|
"begin": "(?i)\\bFOR\\s+",
|
||||||
"beginCaptures": {
|
"beginCaptures": {
|
||||||
"0": { "name": "keyword.control" }
|
"0": {
|
||||||
|
"name": "keyword.control"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
@@ -832,6 +782,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"include": "#number-literal"
|
"include": "#number-literal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#comments"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"end": "(?<!\\\\)\\n"
|
"end": "(?<!\\\\)\\n"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const path = require('path');
|
|||||||
/** @type WebpackConfig */
|
/** @type WebpackConfig */
|
||||||
const extensionConfig = {
|
const extensionConfig = {
|
||||||
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
|
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/
|
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
Reference in New Issue
Block a user