Improved syntax, added full word select
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"comments": {
|
"comments": {
|
||||||
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
||||||
"lineComment": "!"
|
"lineComment": {
|
||||||
|
"comment": "!"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// symbols used as brackets
|
// symbols used as brackets
|
||||||
"brackets": [
|
"brackets": [
|
||||||
|
|||||||
119
package.json
119
package.json
@@ -1,54 +1,69 @@
|
|||||||
{
|
{
|
||||||
"name": "format-4690",
|
"name": "format-4690",
|
||||||
"displayName": "format-4690",
|
"displayName": "format-4690",
|
||||||
"description": "Formatter for 4690 BASIC",
|
"description": "Formatter for 4690 BASIC",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.101.0"
|
"vscode": "^1.101.0"
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
"Programming Languages"
|
"Programming Languages"
|
||||||
],
|
],
|
||||||
"activationEvents": [],
|
"activationEvents": [],
|
||||||
"main": "./dist/extension.js",
|
"main": "./dist/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"languages": [{
|
"languages": [
|
||||||
"id": "4690basic",
|
{
|
||||||
"aliases": ["4690 BASIC", "4690basic"],
|
"id": "4690basic",
|
||||||
"extensions": [".BAS", ".J86"],
|
"aliases": [
|
||||||
"configuration": "./language-configuration.json"
|
"4690 BASIC",
|
||||||
}],
|
"4690basic"
|
||||||
"grammars": [
|
],
|
||||||
{
|
"extensions": [
|
||||||
"language": "4690basic",
|
".BAS",
|
||||||
"scopeName": "source.4690basic",
|
".J86"
|
||||||
"path": "./syntaxes/4690basic.tmLanguage.json"
|
],
|
||||||
}
|
"configuration": "./language-configuration.json"
|
||||||
]
|
}
|
||||||
},
|
],
|
||||||
"scripts": {
|
"grammars": [
|
||||||
"vscode:prepublish": "npm run package",
|
{
|
||||||
"compile": "webpack",
|
"language": "4690basic",
|
||||||
"watch": "webpack --watch",
|
"scopeName": "source.4690basic",
|
||||||
"package": "webpack --mode production --devtool hidden-source-map",
|
"path": "./syntaxes/4690basic.tmLanguage.json"
|
||||||
"compile-tests": "tsc -p . --outDir out",
|
}
|
||||||
"watch-tests": "tsc -p . -w --outDir out",
|
],
|
||||||
"pretest": "npm run compile-tests && npm run compile && npm run lint",
|
"keybindings": [
|
||||||
"lint": "eslint src",
|
{
|
||||||
"test": "vscode-test"
|
"command": "4690basic.selectWord",
|
||||||
},
|
"key": "ctrl+d",
|
||||||
"devDependencies": {
|
"when": "editorTextFocus && editorLangId == 4690basic"
|
||||||
"@types/vscode": "^1.101.0",
|
}
|
||||||
"@types/mocha": "^10.0.10",
|
]
|
||||||
"@types/node": "20.x",
|
},
|
||||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
"scripts": {
|
||||||
"@typescript-eslint/parser": "^8.31.1",
|
"vscode:prepublish": "npm run package",
|
||||||
"eslint": "^9.25.1",
|
"compile": "webpack",
|
||||||
"typescript": "^5.8.3",
|
"watch": "webpack --watch",
|
||||||
"ts-loader": "^9.5.2",
|
"package": "webpack --mode production --devtool hidden-source-map",
|
||||||
"webpack": "^5.99.7",
|
"compile-tests": "tsc -p . --outDir out",
|
||||||
"webpack-cli": "^6.0.1",
|
"watch-tests": "tsc -p . -w --outDir out",
|
||||||
"@vscode/test-cli": "^0.0.10",
|
"pretest": "npm run compile-tests && npm run compile && npm run lint",
|
||||||
"@vscode/test-electron": "^2.5.2"
|
"lint": "eslint src",
|
||||||
}
|
"test": "vscode-test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/vscode": "^1.101.0",
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
|
"@types/node": "20.x",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
||||||
|
"@typescript-eslint/parser": "^8.31.1",
|
||||||
|
"eslint": "^9.25.1",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"ts-loader": "^9.5.2",
|
||||||
|
"webpack": "^5.99.7",
|
||||||
|
"webpack-cli": "^6.0.1",
|
||||||
|
"@vscode/test-cli": "^0.0.10",
|
||||||
|
"@vscode/test-electron": "^2.5.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
339
src/extension.ts
339
src/extension.ts
@@ -1,5 +1,10 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
interface FunctionParameterInfo {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface FunctionDefinition {
|
interface FunctionDefinition {
|
||||||
name: string;
|
name: string;
|
||||||
location: vscode.Location;
|
location: vscode.Location;
|
||||||
@@ -14,7 +19,29 @@ const symbolIndex = new Map<string, FunctionDefinition[]>();
|
|||||||
// File index - maps file URIs to their function definitions
|
// File index - maps file URIs to their function definitions
|
||||||
const fileIndex = new Map<string, FunctionDefinition[]>();
|
const fileIndex = new Map<string, FunctionDefinition[]>();
|
||||||
|
|
||||||
|
function getCustomWordRange(document: vscode.TextDocument, position: vscode.Position) {
|
||||||
|
const line = document.lineAt(position.line);
|
||||||
|
const text = line.text;
|
||||||
|
const char = position.character;
|
||||||
|
|
||||||
|
const wordPattern = /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[$#%]?/g;
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = wordPattern.exec(text)) !== null) {
|
||||||
|
const startChar = match.index;
|
||||||
|
const endChar = match.index + match[0].length;
|
||||||
|
|
||||||
|
if (char >= startChar && char <= endChar) {
|
||||||
|
return new vscode.Range(
|
||||||
|
new vscode.Position(position.line, startChar),
|
||||||
|
new vscode.Position(position.line, endChar)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
|
|
||||||
const selector: vscode.DocumentSelector = { language: '4690basic' };
|
const selector: vscode.DocumentSelector = { language: '4690basic' };
|
||||||
|
|
||||||
const saveWatcher = vscode.workspace.onDidSaveTextDocument(async (document) => {
|
const saveWatcher = vscode.workspace.onDidSaveTextDocument(async (document) => {
|
||||||
@@ -39,6 +66,22 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
// Build initial symbol index
|
// Build initial symbol index
|
||||||
await buildInitialSymbolIndex();
|
await buildInitialSymbolIndex();
|
||||||
|
|
||||||
|
const disposable = vscode.commands.registerCommand('4690basic.selectWord', () => {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor || editor.document.languageId !== '4690basic') return;
|
||||||
|
|
||||||
|
const position = editor.selection.active;
|
||||||
|
const document = editor.document;
|
||||||
|
const wordRange = getCustomWordRange(document, position);
|
||||||
|
|
||||||
|
if (wordRange) {
|
||||||
|
editor.selection = new vscode.Selection(wordRange.start, wordRange.end);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Existing formatter provider (unchanged)
|
// Existing formatter provider (unchanged)
|
||||||
const formatterProvider: vscode.DocumentFormattingEditProvider = {
|
const formatterProvider: vscode.DocumentFormattingEditProvider = {
|
||||||
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] {
|
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] {
|
||||||
@@ -52,205 +95,178 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
let indentLevel = 0;
|
let indentLevel = 0;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
// Helper function to remove comments from a line (but preserve line continuations)
|
// Helper function to remove comments and continuation markers from a line
|
||||||
function removeComments(line: string): string {
|
function cleanLine(line: string): string {
|
||||||
let inQuotes = false;
|
let inQuotes = false;
|
||||||
let quoteChar = '';
|
let quoteChar = '';
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
for (let j = 0; j < line.length; j++) {
|
||||||
|
const char = line[j];
|
||||||
|
|
||||||
for (let i = 0; i < line.length; i++) {
|
|
||||||
const char = line[i];
|
|
||||||
if (!inQuotes && (char === '"' || char === "'")) {
|
if (!inQuotes && (char === '"' || char === "'")) {
|
||||||
inQuotes = true;
|
inQuotes = true;
|
||||||
quoteChar = char;
|
quoteChar = char;
|
||||||
|
result += char;
|
||||||
} else if (inQuotes && char === quoteChar) {
|
} else if (inQuotes && char === quoteChar) {
|
||||||
inQuotes = false;
|
inQuotes = false;
|
||||||
quoteChar = '';
|
quoteChar = '';
|
||||||
|
result += char;
|
||||||
} else if (!inQuotes && char === '!') {
|
} else if (!inQuotes && char === '!') {
|
||||||
return line.substring(0, i).trim();
|
// Comment starts here, ignore rest of line
|
||||||
|
break;
|
||||||
|
} else if (!inQuotes && char === '\\') {
|
||||||
|
// Continuation marker, ignore rest of line
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
result += char;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return line;
|
|
||||||
|
return result.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to check if a line should continue
|
// Helper function to build a complete logical line by looking ahead
|
||||||
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
|
function buildLogicalLine(startIndex: number): { logicalLine: string, physicalLines: string[], endIndex: number } {
|
||||||
const trimmedLine = line.trim();
|
const physicalLines = [];
|
||||||
|
let logicalParts = [];
|
||||||
|
let currentIndex = startIndex;
|
||||||
|
|
||||||
// Skip comment lines that start with backslash (like \REM!!)
|
if (!/(IF|THEN|ENDIF|ELSE|FUNCTION|DEF|SUB|WHILE|FOR)\b/i.test(lines[currentIndex])) {
|
||||||
// These should not be treated as continuation lines
|
return { logicalLine: lines[currentIndex], physicalLines: [lines[currentIndex]], endIndex: currentIndex };
|
||||||
if (/^\s*\\(REM|rem)/i.test(trimmedLine)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const withoutComments = removeComments(line).trim();
|
while (currentIndex < lines.length) {
|
||||||
|
const line = lines[currentIndex];
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
// Check for explicit continuation with \
|
// Skip comment lines that start with backslash (like \REM!!)
|
||||||
if (/^(?:[^"\\]|"[^"]*")*\\/.test(withoutComments)) {
|
if (/^\s*(\\|REM|!)/i.test(trimmedLine)) {
|
||||||
// Special case: if this is a THEN \ line, don't continue
|
physicalLines.push(line);
|
||||||
// Let the next line be processed as its own logical group
|
currentIndex++;
|
||||||
if (/\bTHEN\s*\\/.test(withoutComments)) {
|
continue;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for IF without THEN on the same line
|
|
||||||
if (/^\s*IF\b/i.test(withoutComments) && !/\bTHEN\b/i.test(withoutComments)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
physicalLines.push(line);
|
||||||
let prevIndex = currentIndex - 1;
|
const cleanedLine = cleanLine(line);
|
||||||
while (prevIndex >= 0) {
|
|
||||||
const prevLine = removeComments(allLines[prevIndex]).trim();
|
if (cleanedLine) {
|
||||||
if (prevLine === '') {
|
logicalParts.push(cleanedLine);
|
||||||
prevIndex--;
|
}
|
||||||
continue;
|
|
||||||
}
|
// Check if this line continues (ends with backslash outside quotes/comments)
|
||||||
if (/\\.*$/.test(prevLine) ||
|
let hasContinuation = false;
|
||||||
/^\s*IF\b/i.test(prevLine) ||
|
let inQuotes = false;
|
||||||
/^\s*(AND\b|OR\b|NOT\b|\()/i.test(prevLine)) {
|
|
||||||
return true;
|
for (let j = 0; j < line.length; j++) {
|
||||||
|
const char = line[j];
|
||||||
|
if (!inQuotes && char === '"') {
|
||||||
|
inQuotes = true;
|
||||||
|
} else if (inQuotes && char === '"') {
|
||||||
|
inQuotes = false;
|
||||||
|
} else if (!inQuotes && char === '!') {
|
||||||
|
// Comment starts, no continuation possible after this
|
||||||
|
break;
|
||||||
|
} else if (!inQuotes && char === '\\') {
|
||||||
|
if (!/BEGIN/.test(cleanedLine)) {
|
||||||
|
hasContinuation = true;
|
||||||
|
}
|
||||||
|
const nextLineText = lines[currentIndex + 1];
|
||||||
|
if (nextLineText && /THEN\s*(!|\\)\\/i.test(cleanedLine) && /^\s*(IF|WHILE|FOR)\b/i.test(nextLineText)) {
|
||||||
|
hasContinuation = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex++;
|
||||||
|
|
||||||
|
// If no continuation marker, this logical line is complete
|
||||||
|
if (!hasContinuation) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logicalLine = logicalParts.join(' ').trim();
|
||||||
|
return {
|
||||||
|
logicalLine,
|
||||||
|
physicalLines,
|
||||||
|
endIndex: currentIndex - 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a logical line should start an indent block
|
||||||
|
function shouldStartIndentBlock(logicalLine: string): boolean {
|
||||||
|
|
||||||
|
if (/^\s*((FUNCTION|DEF|SUB|FOR|WHILE)\b)|(BEGIN\b)/i.test(logicalLine)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a logical line should end an indent block
|
||||||
|
function shouldEndIndentBlock(logicalLine: string): boolean {
|
||||||
|
return /^\s*(END\s+FUNCTION|FEND|END\s+SUB|ENDIF|WEND|NEXT)\b/i.test(logicalLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a logical line is ELSE (which decreases then increases indent)
|
||||||
|
function isElseStatement(logicalLine: string): boolean {
|
||||||
|
return /^\s*ELSE\b/i.test(logicalLine) && !/\bBEGIN\b/i.test(logicalLine);
|
||||||
|
}
|
||||||
|
|
||||||
while (i < lines.length) {
|
while (i < lines.length) {
|
||||||
const originalLine = lines[i];
|
|
||||||
const trimmedLine = originalLine.trim();
|
|
||||||
|
|
||||||
// Skip empty lines
|
// Skip empty lines
|
||||||
if (trimmedLine === '') {
|
if (lines[i].trim() === '') {
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect continuation lines
|
// Build complete logical line
|
||||||
let continuationLines = [originalLine];
|
const { logicalLine, physicalLines, endIndex } = buildLogicalLine(i);
|
||||||
let j = i;
|
|
||||||
|
|
||||||
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
// console.log({ logicalLine, physicalLines, endIndex });
|
||||||
if (j + 1 < lines.length) {
|
|
||||||
j++;
|
|
||||||
continuationLines.push(lines[j]);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the complete logical line by joining all parts
|
// Determine indentation changes
|
||||||
let logicalParts: string[] = [];
|
// const isCompound = isCompoundStatement(logicalLine);
|
||||||
for (let k = 0; k < continuationLines.length; k++) {
|
const isElse = isElseStatement(logicalLine);
|
||||||
let part = continuationLines[k].trim();
|
let shouldEnd = shouldEndIndentBlock(logicalLine);
|
||||||
part = removeComments(part);
|
const shouldStart = shouldStartIndentBlock(logicalLine);
|
||||||
if (part.endsWith('\\')) {
|
|
||||||
part = part.slice(0, -1).trim();
|
|
||||||
}
|
|
||||||
if (part) {
|
|
||||||
logicalParts.push(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullLogical = logicalParts.join(' ').trim();
|
// Adjust indent level BEFORE formatting (for closing statements)
|
||||||
const firstLineTrimmed = removeComments(continuationLines[0]).trim();
|
if (isElse || shouldEnd) {
|
||||||
|
|
||||||
// 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 = /^\s*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) ||
|
|
||||||
/^\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);
|
|
||||||
const isCompoundEndifElseIf = /\bENDIF\s+ELSE\s+IF\b/i.test(fullLogical) && containsBegin;
|
|
||||||
|
|
||||||
// Adjust indent level for closing statements BEFORE formatting
|
|
||||||
if (isCompoundEndifElse || isCompoundEndifElseIf) {
|
|
||||||
indentLevel = Math.max(indentLevel - 1, 0);
|
|
||||||
} else if (isClosing) {
|
|
||||||
indentLevel = Math.max(indentLevel - 1, 0);
|
indentLevel = Math.max(indentLevel - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format all lines in this logical group with current indent level
|
shouldEnd = false;
|
||||||
for (let k = 0; k < continuationLines.length; k++) {
|
|
||||||
const lineIndex = i + k;
|
// Format all physical lines in this logical group
|
||||||
const original = continuationLines[k];
|
for (let j = 0; j < physicalLines.length; j++) {
|
||||||
|
const lineIndex = i + j;
|
||||||
|
const original = physicalLines[j];
|
||||||
const trimmed = original.trim();
|
const trimmed = original.trim();
|
||||||
|
|
||||||
if (trimmed === '') continue;
|
if (trimmed === '') continue;
|
||||||
|
|
||||||
// For continuation lines (k > 0), use appropriate indentation
|
// Determine indentation for this line
|
||||||
let lineIndentLevel = indentLevel;
|
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 indent = indentUnit.repeat(lineIndentLevel);
|
||||||
const formatted = indent + trimmed;
|
const formatted = indent + trimmed;
|
||||||
|
|
||||||
if (formatted !== original) {
|
// if (formatted !== original) {
|
||||||
const lineRange = document.lineAt(lineIndex).range;
|
const lineRange = document.lineAt(lineIndex).range;
|
||||||
edits.push(vscode.TextEdit.replace(lineRange, formatted));
|
edits.push(vscode.TextEdit.replace(lineRange, formatted));
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust indent level for opening statements AFTER formatting
|
if (shouldStart && !shouldEnd) {
|
||||||
if (isCompoundEndifElse || isCompoundEndifElseIf) {
|
|
||||||
indentLevel++;
|
|
||||||
} else if (isOpening) {
|
|
||||||
indentLevel++;
|
indentLevel++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to the next logical group
|
// Move to next logical group
|
||||||
i = j + 1;
|
i = endIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits;
|
return edits;
|
||||||
@@ -262,6 +278,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
const definitions: FunctionDefinition[] = [];
|
const definitions: FunctionDefinition[] = [];
|
||||||
const lines = document.getText().split('\n');
|
const lines = document.getText().split('\n');
|
||||||
const identifierRegex = /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?/;
|
const identifierRegex = /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?/;
|
||||||
|
const labelRegex = /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?:/;
|
||||||
|
|
||||||
// Helper function to remove comments from a line
|
// Helper function to remove comments from a line
|
||||||
function removeComments(line: string): string {
|
function removeComments(line: string): string {
|
||||||
@@ -500,26 +517,26 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
position: vscode.Position,
|
position: vscode.Position,
|
||||||
token: vscode.CancellationToken
|
token: vscode.CancellationToken
|
||||||
): Promise<vscode.Definition | undefined> {
|
): Promise<vscode.Definition | undefined> {
|
||||||
const wordRange = document.getWordRangeAtPosition(position, /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?/);
|
const functionWordRange = document.getWordRangeAtPosition(position, /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?/);
|
||||||
if (!wordRange) {
|
if (!functionWordRange) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const word = document.getText(wordRange).toLowerCase();
|
const word = document.getText(functionWordRange).toLowerCase();
|
||||||
const definitions = symbolIndex.get(word);
|
const functionDefinitions = symbolIndex.get(word);
|
||||||
|
|
||||||
if (!definitions || definitions.length === 0) {
|
if (!functionDefinitions || functionDefinitions.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If multiple definitions exist, prefer the one in the current document
|
// If multiple definitions exist, prefer the one in the current document
|
||||||
const localDef = definitions.find(def => def.location.uri.toString() === document.uri.toString());
|
const localDef = functionDefinitions.find(def => def.location.uri.toString() === document.uri.toString());
|
||||||
if (localDef) {
|
if (localDef) {
|
||||||
return localDef.location;
|
return localDef.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise return the first definition found
|
// Otherwise return the first definition found
|
||||||
return definitions[0].location;
|
return functionDefinitions[0].location;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -613,16 +630,12 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
function findFunctionEnd(startLine: number, functionType: 'FUNCTION' | 'DEF' | 'SUB'): number {
|
function findFunctionEnd(startLine: number, functionType: 'FUNCTION' | 'DEF' | 'SUB'): number {
|
||||||
let endPattern: RegExp;
|
let endPattern: RegExp;
|
||||||
|
|
||||||
// console.log('functionType: ', functionType);
|
|
||||||
|
|
||||||
if (functionType === 'FUNCTION' || functionType === 'DEF') {
|
if (functionType === 'FUNCTION' || functionType === 'DEF') {
|
||||||
endPattern = /^\s*(END\s+FUNCTION)|(FEND)\b/i;
|
endPattern = /^\s*(END\s+FUNCTION)|(FEND)\b/i;
|
||||||
} else { // SUB
|
} else { // SUB
|
||||||
endPattern = /^\s*END\s+SUB\b/i;
|
endPattern = /^\s*END\s+SUB\b/i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('endPattern: ', endPattern);
|
|
||||||
|
|
||||||
for (let i = startLine + 1; i < lines.length; i++) {
|
for (let i = startLine + 1; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
|
|
||||||
@@ -636,17 +649,12 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('Evaluating for end: ', line);
|
|
||||||
|
|
||||||
// Check if this line matches the end pattern
|
// Check if this line matches the end pattern
|
||||||
const endMatch = endPattern.exec(line);
|
const endMatch = endPattern.exec(line);
|
||||||
if (endMatch) {
|
if (endMatch) {
|
||||||
|
|
||||||
// console.log('Found match: ', endMatch);
|
|
||||||
|
|
||||||
// Check if the end keyword is not commented out
|
// Check if the end keyword is not commented out
|
||||||
if (!isKeywordCommentedOut(line, endMatch)) {
|
if (!isKeywordCommentedOut(line, endMatch)) {
|
||||||
// console.log('Returning i');
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,21 +691,13 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('Current line: ', trimmedLine);
|
|
||||||
|
|
||||||
// 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';
|
||||||
|
|
||||||
// console.log('functionMatch: ', functionMatch);
|
|
||||||
// console.log('Definition type: ', defType);
|
|
||||||
|
|
||||||
// Find the identifier after the function keyword
|
// Find the identifier after the function keyword
|
||||||
const afterKeyword = line.substring(functionMatch[0].length).trim();
|
const afterKeyword = line.substring(functionMatch[0].length).trim();
|
||||||
const nameMatch = identifierRegex.exec(afterKeyword);
|
const nameMatch = identifierRegex.exec(afterKeyword);
|
||||||
|
|
||||||
// console.log('afterKeyword: ', afterKeyword);
|
|
||||||
// console.log('nameMatch: ', nameMatch);
|
|
||||||
|
|
||||||
if (!nameMatch) {
|
if (!nameMatch) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -805,7 +805,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
vscode.languages.registerDocumentSymbolProvider(selector, documentSymbolProvider),
|
vscode.languages.registerDocumentSymbolProvider(selector, documentSymbolProvider),
|
||||||
vscode.languages.registerHoverProvider(selector, hoverProvider),
|
vscode.languages.registerHoverProvider(selector, hoverProvider),
|
||||||
saveWatcher,
|
saveWatcher,
|
||||||
fsWatcher
|
fsWatcher,
|
||||||
|
disposable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
"name": "string.regexp",
|
"name": "string.regexp",
|
||||||
"match": "\\b[\\w\\.]+:"
|
"match": "\\b[\\w\\.]+:"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "meta",
|
||||||
|
"match": "\\s:"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"include": "#function-decl"
|
"include": "#function-decl"
|
||||||
},
|
},
|
||||||
@@ -42,6 +46,12 @@
|
|||||||
{
|
{
|
||||||
"include": "#if-statement-end"
|
"include": "#if-statement-end"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"include": "#else"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": "#then"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"include": "#for-statement"
|
"include": "#for-statement"
|
||||||
},
|
},
|
||||||
@@ -407,7 +417,7 @@
|
|||||||
},
|
},
|
||||||
"logical-operator": {
|
"logical-operator": {
|
||||||
"name": "keyword.operator.logical",
|
"name": "keyword.operator.logical",
|
||||||
"match": "(?i)\\b(NOT|AND|OR|XOR)\\b"
|
"match": "(?i)\\b(NOT|AND|OR|XOR)($|\\n|\\s\\()"
|
||||||
},
|
},
|
||||||
"string-operator": {
|
"string-operator": {
|
||||||
"name": "keyword.operator",
|
"name": "keyword.operator",
|
||||||
@@ -513,7 +523,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "support.function",
|
"name": "support.function",
|
||||||
"match": "(?i)\\b(NULL|TRUE|FALSE|READ|WRITE|KEY)\\b[^%\\$\\.]"
|
"match": "(?i)\\b(NULL|TRUE|FALSE|READ|WRITE|KEY|NOWRITE|NODEL)\\b[^%\\$\\.]"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -633,127 +643,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"if-statement": {
|
"if-statement": {
|
||||||
"name": "meta.if",
|
"name": "keyword.control",
|
||||||
"begin": "(?i)\\b(IF)",
|
"match": "(?i)\\b(IF|BEGIN)\\b"
|
||||||
"beginCaptures": {
|
|
||||||
"0": {
|
|
||||||
"name": "keyword.control"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"include": "#math-function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#string-function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#number-literal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#arithmetic-operator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#relational-operator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#logical-operator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#keywords"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#strings"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "(?i)THEN"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "(?i)ELSE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "(?i)(BEGIN)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#identifier"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#line-continuation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#comments"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match": "\\s+",
|
|
||||||
"name": "text.whitespace"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"end": "(?<!\\\\)(\\n)"
|
|
||||||
},
|
},
|
||||||
"if-statement-end": {
|
"if-statement-end": {
|
||||||
"name": "meta.if.end",
|
"name": "keyword.control",
|
||||||
"begin": "(?i)ENDIF",
|
"match": "(?i)ENDIF"
|
||||||
"beginCaptures": {
|
},
|
||||||
"0": {
|
"else": {
|
||||||
"name": "keyword.control"
|
"name": "keyword.control",
|
||||||
}
|
"match": "(?i)ELSE"
|
||||||
},
|
},
|
||||||
"patterns": [
|
"then": {
|
||||||
{
|
"name": "keyword.control",
|
||||||
"name": "keyword.control",
|
"match": "(?i)THEN"
|
||||||
"match": "ELSE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#math-function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#string-function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#number-literal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#arithmetic-operator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#relational-operator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#logical-operator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#keywords"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#strings"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "THEN|then"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "(?i)BEGIN"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "(?i)ELSE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.control",
|
|
||||||
"match": "(?i)IF"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#line-continuation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#comments"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"end": "(?<!\\\\)\\n"
|
|
||||||
},
|
},
|
||||||
"for-statement": {
|
"for-statement": {
|
||||||
"name": "meta.for",
|
"name": "meta.for",
|
||||||
|
|||||||
Reference in New Issue
Block a user