- 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."
89 lines
2.6 KiB
JavaScript
89 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
var utils = require('../utils.js');
|
|
require('eslint-visitor-keys');
|
|
require('espree');
|
|
require('estraverse');
|
|
|
|
var newlinePerChainedCall = utils.createRule({
|
|
name: "newline-per-chained-call",
|
|
package: "js",
|
|
meta: {
|
|
type: "layout",
|
|
docs: {
|
|
description: "Require a newline after each call in a method chain"
|
|
},
|
|
fixable: "whitespace",
|
|
schema: [{
|
|
type: "object",
|
|
properties: {
|
|
ignoreChainWithDepth: {
|
|
type: "integer",
|
|
minimum: 1,
|
|
maximum: 10,
|
|
default: 2
|
|
}
|
|
},
|
|
additionalProperties: false
|
|
}],
|
|
messages: {
|
|
expected: "Expected line break before `{{callee}}`."
|
|
}
|
|
},
|
|
create(context) {
|
|
const options = context.options[0] || {};
|
|
const ignoreChainWithDepth = options.ignoreChainWithDepth || 2;
|
|
const sourceCode = context.sourceCode;
|
|
function getPrefix(node) {
|
|
if (node.computed) {
|
|
if (node.optional)
|
|
return "?.[";
|
|
return "[";
|
|
}
|
|
if (node.optional)
|
|
return "?.";
|
|
return ".";
|
|
}
|
|
function getPropertyText(node) {
|
|
const prefix = getPrefix(node);
|
|
const lines = sourceCode.getText(node.property).split(utils.LINEBREAK_MATCHER);
|
|
const suffix = node.computed && lines.length === 1 ? "]" : "";
|
|
return prefix + lines[0] + suffix;
|
|
}
|
|
return {
|
|
"CallExpression:exit": function(node) {
|
|
const callee = utils.skipChainExpression(node.callee);
|
|
if (callee.type !== "MemberExpression")
|
|
return;
|
|
let parent = utils.skipChainExpression(callee.object);
|
|
let depth = 1;
|
|
while (parent && "callee" in parent && parent.callee) {
|
|
depth += 1;
|
|
const parentCallee = utils.skipChainExpression(parent.callee);
|
|
if (!("object" in parentCallee))
|
|
break;
|
|
parent = utils.skipChainExpression(parentCallee.object);
|
|
}
|
|
if (depth > ignoreChainWithDepth && utils.isTokenOnSameLine(callee.object, callee.property)) {
|
|
const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, utils.isNotClosingParenToken);
|
|
context.report({
|
|
node: callee.property,
|
|
loc: {
|
|
start: firstTokenAfterObject.loc.start,
|
|
end: callee.loc.end
|
|
},
|
|
messageId: "expected",
|
|
data: {
|
|
callee: getPropertyText(callee)
|
|
},
|
|
fix(fixer) {
|
|
return fixer.insertTextBefore(firstTokenAfterObject, "\n");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
module.exports = newlinePerChainedCall;
|