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';
|
import * as vscode from 'vscode';
|
||||||
|
import { FunctionDefinition, parseFunctionDefinitions } from './functions';
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global symbol index - maps lowercase function names to their definitions
|
// Global symbol index - maps lowercase function names to their definitions
|
||||||
const symbolIndex = new Map<string, FunctionDefinition[]>();
|
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
|
// Function to build the initial symbol index
|
||||||
async function buildInitialSymbolIndex(): Promise<void> {
|
async function buildInitialSymbolIndex(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -785,7 +613,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
// Build function signature with parameters
|
// Build function signature with parameters
|
||||||
let signature = `${definition.type} ${definition.name}`;
|
let signature = `${definition.type} ${definition.name}`;
|
||||||
if (definition.parameterCount > 0) {
|
if (definition.parameterCount > 0) {
|
||||||
signature += `(${definition.parameters.join(', ')})`;
|
signature += `(${definition.parameters.map(param => `${param.name}: ${param.type}`).join(', ')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
content.appendCodeblock(signature, '4690basic');
|
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