ifc-language-server/node_modules/@stylistic/eslint-plugin/dist/rules/function-call-spacing.js
Ryan Schultz 8afacf268a Implemented a working Language Server Protocol (LSP) for IFC files with:
- Hover provider showing entity information and type
- Go-to-definition (F12) for entity references
- Basic IFC file validation (ISO-10303-21 header check)
- Entity parsing with regex-based detection
- Proper CommonJS module system (avoiding ES module issues)

This replaces the broken baseline from ifc-developer-tools which had:
- Non-functional ES module configuration
- Circular dependency issues
- Parser crashes
- Non-working PositionVisitor

Built on Microsoft's LSP example template for a clean, maintainable foundation.

Next: Add hierarchical entity dependency tree in hover tooltip."
2025-12-07 10:20:07 -06:00

141 lines
4.1 KiB
JavaScript

'use strict';
var utils = require('../utils.js');
var astUtils = require('@typescript-eslint/utils/ast-utils');
require('eslint-visitor-keys');
require('espree');
require('estraverse');
var functionCallSpacing = utils.createRule({
name: "function-call-spacing",
package: "ts",
meta: {
type: "layout",
docs: {
description: "Require or disallow spacing between function identifiers and their invocations"
},
fixable: "whitespace",
schema: {
anyOf: [
{
type: "array",
items: [
{
type: "string",
enum: ["never"]
}
],
minItems: 0,
maxItems: 1
},
{
type: "array",
items: [
{
type: "string",
enum: ["always"]
},
{
type: "object",
properties: {
allowNewlines: {
type: "boolean"
}
},
additionalProperties: false
}
],
minItems: 0,
maxItems: 2
}
]
},
messages: {
unexpectedWhitespace: "Unexpected whitespace between function name and paren.",
unexpectedNewline: "Unexpected newline between function name and paren.",
missing: "Missing space between function name and paren."
}
},
defaultOptions: ["never", {}],
create(context, [option, config]) {
const sourceCode = context.sourceCode;
const text = sourceCode.getText();
function checkSpacing(node) {
const isOptionalCall = astUtils.isOptionalCallExpression(node);
const closingParenToken = sourceCode.getLastToken(node);
const lastCalleeTokenWithoutPossibleParens = sourceCode.getLastToken(
node.typeArguments ?? node.callee
);
const openingParenToken = sourceCode.getFirstTokenBetween(
lastCalleeTokenWithoutPossibleParens,
closingParenToken,
astUtils.isOpeningParenToken
);
if (!openingParenToken || openingParenToken.range[1] >= node.range[1]) {
return;
}
const lastCalleeToken = sourceCode.getTokenBefore(
openingParenToken,
astUtils.isNotOptionalChainPunctuator
);
const textBetweenTokens = text.slice(lastCalleeToken.range[1], openingParenToken.range[0]).replace(/\/\*.*?\*\//gu, "");
const hasWhitespace = /\s/u.test(textBetweenTokens);
const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
if (option === "never") {
if (hasWhitespace) {
return context.report({
node,
loc: lastCalleeToken.loc.start,
messageId: "unexpectedWhitespace",
fix(fixer) {
if (!hasNewline && !isOptionalCall) {
return fixer.removeRange([
lastCalleeToken.range[1],
openingParenToken.range[0]
]);
}
return null;
}
});
}
} else if (isOptionalCall) {
if (hasWhitespace || hasNewline) {
context.report({
node,
loc: lastCalleeToken.loc.start,
messageId: "unexpectedWhitespace"
});
}
} else {
if (!hasWhitespace) {
context.report({
node,
loc: lastCalleeToken.loc.start,
messageId: "missing",
fix(fixer) {
return fixer.insertTextBefore(openingParenToken, " ");
}
});
} else if (!config.allowNewlines && hasNewline) {
context.report({
node,
loc: lastCalleeToken.loc.start,
messageId: "unexpectedNewline",
fix(fixer) {
return fixer.replaceTextRange(
[lastCalleeToken.range[1], openingParenToken.range[0]],
" "
);
}
});
}
}
}
return {
CallExpression: checkSpacing,
NewExpression: checkSpacing
};
}
});
module.exports = functionCallSpacing;