Added function parameter type detection
This commit is contained in:
176
src/extension.ts
176
src/extension.ts
@@ -1,17 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
interface FunctionParameterInfo {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface FunctionDefinition {
|
||||
name: string;
|
||||
location: vscode.Location;
|
||||
type: 'FUNCTION' | 'SUB' | 'DEF';
|
||||
parameters: string[]; // Array of parameter names
|
||||
parameterCount: number; // Number of parameters
|
||||
}
|
||||
import { FunctionDefinition, parseFunctionDefinitions } from './functions';
|
||||
|
||||
// Global symbol index - maps lowercase function names to their definitions
|
||||
const symbolIndex = new Map<string, FunctionDefinition[]>();
|
||||
@@ -273,166 +261,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
};
|
||||
|
||||
// Function to parse function definitions from a document
|
||||
function parseFunctionDefinitions(document: vscode.TextDocument): FunctionDefinition[] {
|
||||
const definitions: FunctionDefinition[] = [];
|
||||
const lines = document.getText().split('\n');
|
||||
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
|
||||
function removeComments(line: string): string {
|
||||
let inQuotes = false;
|
||||
let quoteChar = '';
|
||||
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
if (!inQuotes && (char === '"' || char === "'")) {
|
||||
inQuotes = true;
|
||||
quoteChar = char;
|
||||
} else if (inQuotes && char === quoteChar) {
|
||||
inQuotes = false;
|
||||
quoteChar = '';
|
||||
} else if (!inQuotes && char === '!') {
|
||||
return line.substring(0, i).trim();
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
// Helper function to check if a line should continue
|
||||
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
|
||||
const withoutComments = removeComments(line).trim();
|
||||
|
||||
// Check for explicit continuation with \
|
||||
if (withoutComments.endsWith('\\')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For function definitions, also check if we have incomplete parentheses
|
||||
if (/^\s*(FUNCTION|DEF|SUB)\b/i.test(withoutComments)) {
|
||||
const openParens = (withoutComments.match(/\(/g) || []).length;
|
||||
const closeParens = (withoutComments.match(/\)/g) || []).length;
|
||||
if (openParens > closeParens) {
|
||||
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;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
while (i < lines.length) {
|
||||
const originalLine = lines[i];
|
||||
const trimmedLine = originalLine.trim();
|
||||
|
||||
// Skip empty lines
|
||||
if (trimmedLine === '') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this line starts a function definition
|
||||
const functionMatch = /^\s*(FUNCTION|DEF|SUB)\b/i.exec(trimmedLine);
|
||||
if (!functionMatch) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect continuation lines for this function definition
|
||||
let continuationLines = [originalLine];
|
||||
let j = i;
|
||||
|
||||
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
||||
if (j + 1 < lines.length) {
|
||||
j++;
|
||||
continuationLines.push(lines[j]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the complete logical line by joining all parts
|
||||
let logicalParts: string[] = [];
|
||||
for (let k = 0; k < continuationLines.length; k++) {
|
||||
let part = continuationLines[k].trim();
|
||||
part = removeComments(part);
|
||||
if (part.endsWith('\\')) {
|
||||
part = part.slice(0, -1).trim();
|
||||
}
|
||||
if (part) {
|
||||
logicalParts.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
const fullLogical = logicalParts.join(' ').trim();
|
||||
|
||||
// Check if this is not an external declaration
|
||||
if (!/\bEXTERNAL\s*$/i.test(fullLogical)) {
|
||||
// Extract the function type and name
|
||||
const defType = functionMatch[1].toUpperCase() as 'FUNCTION' | 'SUB' | 'DEF';
|
||||
|
||||
// Find the identifier after the function keyword
|
||||
const afterKeyword = fullLogical.substring(functionMatch[0].length).trim();
|
||||
const nameMatch = identifierRegex.exec(afterKeyword);
|
||||
|
||||
if (nameMatch) {
|
||||
const functionName = nameMatch[0];
|
||||
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,
|
||||
parameters: parameters,
|
||||
parameterCount: parameters.length
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next logical group
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
// Function to build the initial symbol index
|
||||
async function buildInitialSymbolIndex(): Promise<void> {
|
||||
try {
|
||||
@@ -785,7 +613,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
// Build function signature with parameters
|
||||
let signature = `${definition.type} ${definition.name}`;
|
||||
if (definition.parameterCount > 0) {
|
||||
signature += `(${definition.parameters.join(', ')})`;
|
||||
signature += `(${definition.parameters.map(param => `${param.name}: ${param.type}`).join(', ')})`;
|
||||
}
|
||||
|
||||
content.appendCodeblock(signature, '4690basic');
|
||||
|
||||
222
src/functions.ts
Normal file
222
src/functions.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface FunctionDefinition {
|
||||
name: string;
|
||||
location: vscode.Location;
|
||||
type: 'FUNCTION' | 'SUB' | 'DEF';
|
||||
parameters: FunctionParameterInfo[]; // Array of parameter names
|
||||
parameterCount: number; // Number of parameters
|
||||
}
|
||||
|
||||
interface FunctionParameterInfo {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
const typeDeclarationBeginRegex = /^\s*(INTEGER(\*[124])?|REAL|STRING)/i;
|
||||
|
||||
// Function to parse function definitions from a document
|
||||
export function parseFunctionDefinitions(document: vscode.TextDocument): FunctionDefinition[] {
|
||||
const definitions: FunctionDefinition[] = [];
|
||||
const lines = document.getText().split('\n');
|
||||
const identifierRegex = /(\?|[a-zA-Z])([a-zA-Z0-9#\.]*)[\$#%]?/;
|
||||
|
||||
let i = 0;
|
||||
while (i < lines.length) {
|
||||
const originalLine = lines[i];
|
||||
const trimmedLine = originalLine.trim();
|
||||
|
||||
// Skip empty lines
|
||||
if (trimmedLine === '') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this line starts a function definition
|
||||
const functionMatch = /^\s*(FUNCTION|DEF|SUB)\b/i.exec(trimmedLine);
|
||||
if (!functionMatch) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect continuation lines for this function definition
|
||||
let continuationLines = [originalLine];
|
||||
let j = i;
|
||||
|
||||
while (j < lines.length && shouldContinue(lines[j], lines, j)) {
|
||||
if (j + 1 < lines.length) {
|
||||
j++;
|
||||
continuationLines.push(lines[j]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the complete logical line by joining all parts
|
||||
let logicalParts: string[] = [];
|
||||
for (let k = 0; k < continuationLines.length; k++) {
|
||||
let part = continuationLines[k].trim();
|
||||
part = removeComments(part);
|
||||
if (part.endsWith('\\')) {
|
||||
part = part.slice(0, -1).trim();
|
||||
}
|
||||
if (part) {
|
||||
logicalParts.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
const fullLogical = logicalParts.join(' ').trim();
|
||||
|
||||
// Check if this is not an external declaration
|
||||
if (!/\bEXTERNAL\s*$/i.test(fullLogical)) {
|
||||
// Extract the function type and name
|
||||
const defType = functionMatch[1].toUpperCase() as 'FUNCTION' | 'SUB' | 'DEF';
|
||||
|
||||
// Find the identifier after the function keyword
|
||||
const afterKeyword = fullLogical.substring(functionMatch[0].length).trim();
|
||||
const nameMatch = identifierRegex.exec(afterKeyword);
|
||||
|
||||
if (nameMatch) {
|
||||
const functionName = nameMatch[0];
|
||||
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, identifierRegex);
|
||||
}
|
||||
|
||||
const paramterTypes = parseTypeDeclarations(i, parameters, lines);
|
||||
|
||||
definitions.push({
|
||||
name: functionName,
|
||||
location: location,
|
||||
type: defType,
|
||||
parameters: paramterTypes,
|
||||
parameterCount: parameters.length
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Move to the next logical group
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
// Helper function to remove comments from a line
|
||||
function removeComments(line: string): string {
|
||||
let inQuotes = false;
|
||||
let quoteChar = '';
|
||||
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
if (!inQuotes && (char === '"' || char === "'")) {
|
||||
inQuotes = true;
|
||||
quoteChar = char;
|
||||
} else if (inQuotes && char === quoteChar) {
|
||||
inQuotes = false;
|
||||
quoteChar = '';
|
||||
} else if (!inQuotes && char === '!') {
|
||||
return line.substring(0, i).trim();
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
// Helper function to check if a line should continue
|
||||
function shouldContinue(line: string, allLines: string[], currentIndex: number): boolean {
|
||||
const withoutComments = removeComments(line).trim();
|
||||
|
||||
// Check for explicit continuation with \
|
||||
if (withoutComments.endsWith('\\')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For function definitions, also check if we have incomplete parentheses
|
||||
if (/^\s*(FUNCTION|DEF|SUB)\b/i.test(withoutComments)) {
|
||||
const openParens = (withoutComments.match(/\(/g) || []).length;
|
||||
const closeParens = (withoutComments.match(/\)/g) || []).length;
|
||||
if (openParens > closeParens) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function to parse parameters from a parameter string
|
||||
function parseParameters(paramString: string, identifierRegex: RegExp): 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;
|
||||
}
|
||||
|
||||
// Helper function to parse parameter types for the defined funtion.
|
||||
function parseTypeDeclarations(startLine: number, parameters: string[], lines: string[], ): FunctionParameterInfo[] {
|
||||
const enhancedParameters: FunctionParameterInfo[] = parameters.map(param => ({ name: param, type: '?' }));
|
||||
let currentLine = startLine + 1;
|
||||
|
||||
let line = getFullLogicalLine(lines, currentLine);
|
||||
let currentType = '';
|
||||
let regexMatch = typeDeclarationBeginRegex.exec(line[0]);
|
||||
|
||||
while (regexMatch) {
|
||||
const identifiers = line[0].split(',').map(ident => ident.trim());
|
||||
identifiers[0] = identifiers[0].replace(/\s+/g, ' ').split(' ')[1];
|
||||
currentType = regexMatch[0].trim().toUpperCase();
|
||||
|
||||
enhancedParameters.forEach(ep => {
|
||||
if (identifiers.indexOf(ep.name) > -1 && ep.type === '?') {
|
||||
ep.type = currentType;
|
||||
}
|
||||
});
|
||||
|
||||
currentLine += line[1];
|
||||
line = getFullLogicalLine(lines, currentLine);
|
||||
regexMatch = typeDeclarationBeginRegex.exec(line[0]);
|
||||
}
|
||||
|
||||
return enhancedParameters;
|
||||
|
||||
}
|
||||
|
||||
function getFullLogicalLine(lines: string[], startLine: number): [string, number] {
|
||||
let fullLogicalLine = '';
|
||||
let currentCleanLogicalLine = removeComments(lines[startLine]).trim();
|
||||
let linesConsumed = 0;
|
||||
|
||||
while (/\\/.test(currentCleanLogicalLine)) {
|
||||
fullLogicalLine += ' ' + currentCleanLogicalLine.substring(0, currentCleanLogicalLine.indexOf('\\'));
|
||||
linesConsumed++;
|
||||
startLine++;
|
||||
currentCleanLogicalLine = removeComments(lines[startLine]).trim();
|
||||
}
|
||||
fullLogicalLine += ' ' + currentCleanLogicalLine;
|
||||
return [fullLogicalLine.replaceAll('\\', ''), linesConsumed + 1];
|
||||
}
|
||||
Reference in New Issue
Block a user