diff --git a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts index acf4f8b441..45b6991021 100644 --- a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts +++ b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts @@ -44,8 +44,8 @@ describe("getLintAnnotations()", () => { const { LINT, PARSE } = PropertyEvaluationErrorType; const { ERROR, WARNING } = Severity; it("should return proper annotations", () => { - const value = `Hello {{ world == test }}`; - const errors: EvaluationError[] = [ + const value1 = `Hello {{ world == test }}`; + const errors1: EvaluationError[] = [ { errorType: LINT, raw: @@ -87,8 +87,8 @@ describe("getLintAnnotations()", () => { }, ]; - const res = getLintAnnotations(value, errors, {}); - expect(res).toEqual([ + const res1 = getLintAnnotations(value1, errors1, {}); + expect(res1).toEqual([ { from: { line: 0, @@ -126,6 +126,40 @@ describe("getLintAnnotations()", () => { severity: "warning", }, ]); + + /// 2 + const value2 = `hss{{hss}}`; + const errors2: EvaluationError[] = [ + { + errorType: LINT, + raw: + "\n function closedFunction () {\n const result = hss\n return result;\n }\n closedFunction.call(THIS_CONTEXT)\n ", + severity: ERROR, + errorMessage: "'hss' is not defined.", + errorSegment: " const result = hss", + originalBinding: "{{hss}}", + variables: ["hss", null, null, null], + code: "W117", + line: 0, + ch: 1, + }, + ]; + + const res2 = getLintAnnotations(value2, errors2, {}); + expect(res2).toEqual([ + { + from: { + line: 0, + ch: 5, + }, + to: { + line: 0, + ch: 8, + }, + message: "'hss' is not defined.", + severity: "error", + }, + ]); }); it("should return correct annotation with newline in original binding", () => { diff --git a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts index 256816583f..e9ccd58fa5 100644 --- a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts +++ b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts @@ -2,6 +2,7 @@ import { last, isNumber, isEmpty } from "lodash"; import { Annotation, Position } from "codemirror"; import { EvaluationError, + isDynamicValue, PropertyEvaluationErrorType, } from "utils/DynamicBindingUtils"; import { Severity } from "entities/AppsmithConsole"; @@ -117,6 +118,11 @@ export const getLintAnnotations = ( const annotations: Annotation[] = []; const lintErrors = filterLintErrors(errors, contextData); const lines = value.split("\n"); + + // The binding position of every valid JS Object is constant, so we need not + // waste time checking for position of binding. + // For JS Objects not starting with the expected "export default" statement, we return early + // with a "invalid start statement" lint error if ( isJSObject && !isEmpty(lines) && @@ -167,8 +173,19 @@ export const getLintAnnotations = ( for (const bindingLocation of bindingPositions) { const currentLine = bindingLocation.line + line; const lineContent = lines[currentLine] || ""; - const currentCh = - bindingLocation.line !== currentLine ? ch : bindingLocation.ch + ch; + let currentCh: number; + + // for case where "{{" is in the same line as the lint error + if (bindingLocation.line === currentLine) { + currentCh = + bindingLocation.ch + + ch + + // Add 2 to account for "{{", if binding is a dynamicValue (NB: JS Objects are dynamicValues without "{{}}") + (isDynamicValue(originalBinding) ? 2 : 0); + } else { + currentCh = ch; + } + // Jshint counts \t as two characters and codemirror counts it as 1. // So we need to subtract number of tabs to get accurate position const tabs = lineContent.slice(0, currentCh).match(/\t/g)?.length || 0; diff --git a/app/client/src/workers/Lint/index.ts b/app/client/src/workers/Lint/index.ts index 2aa21b0672..bb5bd59de0 100644 --- a/app/client/src/workers/Lint/index.ts +++ b/app/client/src/workers/Lint/index.ts @@ -15,11 +15,7 @@ import { removeLintErrorsFromEntityProperty, } from "workers/evaluationUtils"; -import { - getJSSnippetToLint, - getLintingErrors, - pathRequiresLinting, -} from "./utils"; +import { getJSToLint, getLintingErrors, pathRequiresLinting } from "./utils"; interface LintTreeArgs { unEvalTree: DataTree; @@ -136,19 +132,21 @@ const lintBindingPath = ( ); if (stringSegments) { - jsSnippets.map((jsSnippet) => { - const jsSnippetToLint = getJSSnippetToLint( - entity, - jsSnippet, - propertyPath, - ); + jsSnippets.map((jsSnippet, index) => { if (jsSnippet) { + const jsSnippetToLint = getJSToLint(entity, jsSnippet, propertyPath); + // {{user's code}} + const originalBinding = getJSToLint( + entity, + stringSegments[index], + propertyPath, + ); const scriptType = getScriptType(false, false); const scriptToLint = getScriptToEval(jsSnippetToLint, scriptType); lintErrors = getLintingErrors( scriptToLint, globalData, - jsSnippetToLint, + originalBinding, scriptType, ); } diff --git a/app/client/src/workers/Lint/utils.ts b/app/client/src/workers/Lint/utils.ts index 8386c792e7..aae80d4d7e 100644 --- a/app/client/src/workers/Lint/utils.ts +++ b/app/client/src/workers/Lint/utils.ts @@ -55,7 +55,8 @@ export const pathRequiresLinting = ( return requiresLinting; }; -export const getJSSnippetToLint = ( +// Removes "export default" statement from js Object +export const getJSToLint = ( entity: DataTreeEntity, snippet: string, propertyPath: string, @@ -97,6 +98,7 @@ function getEvaluationScriptPosition(scriptType: EvaluationScriptType) { export const getLintingErrors = ( script: string, data: Record, + // {{user's code}} originalBinding: string, scriptType: EvaluationScriptType, ): EvaluationError[] => {