Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0382d29
show lint errors as warnings
vnodecg Jul 14, 2021
1d2678b
Merge branch 'release' into feature/linting-errors
vnodecg Jul 14, 2021
966c446
Merge branch 'release' into feature/linting-errors
vnodecg Jul 20, 2021
733361d
add initial code
vnodecg Jul 27, 2021
6e766ac
Merge remote-tracking branch 'origin/release' into feature-lint-errors
vnodecg Jul 27, 2021
15cf1e2
adjust editor positions
vnodecg Jul 27, 2021
07b8235
update pr comments
vnodecg Jul 27, 2021
7ae6b66
mark jshint errors
vnodecg Jul 29, 2021
4631e60
Merge remote-tracking branch 'origin/release' into FEATURE/linting-er…
vnodecg Jul 29, 2021
d8ec26c
reset changes
vnodecg Jul 30, 2021
81c675c
remove unused prop
vnodecg Jul 30, 2021
c870580
fix test errors
vnodecg Jul 30, 2021
36ccf2c
remove unused imports
vnodecg Jul 31, 2021
8024f66
dont show warning in the error counter
vnodecg Aug 2, 2021
336a59e
show yellow if warning
vnodecg Aug 2, 2021
4609742
remove active error functionality
vnodecg Aug 3, 2021
4b85ec8
update linter to use async functionality
vnodecg Aug 4, 2021
fe79c2e
update linter messages
vnodecg Aug 5, 2021
8983554
update binding positions
vnodecg Aug 10, 2021
106d78d
fix evaluate tests
vnodecg Aug 10, 2021
c6219f1
Merge branch 'release' into feature/Show-lint-errors-in-codeeditor
vnodecg Aug 12, 2021
7c3cec6
Merge branch 'feature/linting-errors' into feature/Show-lint-errors-i…
vnodecg Aug 12, 2021
ca40e4c
dont show undefined errors in debugger
vnodecg Aug 12, 2021
35989b4
move lint code to separate file
vnodecg Aug 12, 2021
afbddef
update testes
vnodecg Aug 12, 2021
a6dde5e
remove unused import
vnodecg Aug 12, 2021
0393726
update tests
vnodecg Aug 13, 2021
97aa7bc
address pr comments
vnodecg Aug 16, 2021
8c89473
add comment to explain why
vnodecg Aug 16, 2021
22c109b
Merge branch 'release' into feature/Show-lint-errors-in-codeeditor
vnodecg Aug 17, 2021
6fe0c95
Merge branch 'release' into feature/Show-lint-errors-in-codeeditor
vnodecg Aug 17, 2021
b82c8e5
replace proper regex
vnodecg Aug 19, 2021
ed9f8fe
fix undefined message error
vnodecg Aug 19, 2021
6c6793a
Update styling for warnings
vnodecg Aug 20, 2021
31d4b06
Merge branch 'release' into feature/Show-lint-errors-in-codeeditor
hetunandu Aug 25, 2021
08085d2
Fix crashing error
hetunandu Aug 25, 2021
d9d125c
Add feature flags
hetunandu Aug 25, 2021
b27aa58
Merge branch 'release' into feature/Show-lint-errors-in-codeeditor
hetunandu Aug 25, 2021
45ce0cf
fix Debugger error counts checks
hetunandu Aug 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions app/client/src/components/editorComponents/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { AppState } from "reducers";
import CodeMirror, { EditorConfiguration } from "codemirror";
import CodeMirror, {
Annotation,
EditorConfiguration,
UpdateLintingCallback,
} from "codemirror";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/duotone-dark.css";
import "codemirror/theme/duotone-light.css";
Expand All @@ -12,6 +16,9 @@ import "codemirror/addon/edit/closebrackets";
import "codemirror/addon/display/autorefresh";
import "codemirror/addon/mode/multiplex";
import "codemirror/addon/tern/tern.css";
import "codemirror/addon/lint/lint";
import "codemirror/addon/lint/lint.css";

import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors";
import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup";
import { WrappedFieldInputProps } from "redux-form";
Expand Down Expand Up @@ -70,6 +77,8 @@ import { getPluginIdToImageLocation } from "sagas/selectors";
import { ExpectedValueExample } from "utils/validation/common";
import { getRecentEntityIds } from "selectors/globalSearchSelectors";
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
import { getLintAnnotations } from "./lintHelpers";
import getFeatureFlags from "utils/featureFlags";

const AUTOCOMPLETE_CLOSE_KEY_CODES = [
"Enter",
Expand Down Expand Up @@ -147,8 +156,9 @@ class CodeEditor extends Component<Props, State> {
codeEditorTarget = React.createRef<HTMLDivElement>();
editor!: CodeMirror.Editor;
hinters: Hinter[] = [];
annotations: Annotation[] = [];
updateLintingCallback: UpdateLintingCallback | undefined;
private editorWrapperRef = React.createRef<HTMLDivElement>();

constructor(props: Props) {
super(props);
this.state = {
Expand Down Expand Up @@ -176,6 +186,13 @@ class CodeEditor extends Component<Props, State> {
scrollbarStyle:
this.props.size !== EditorSize.COMPACT ? "native" : "null",
placeholder: this.props.placeholder,
lint: {
getAnnotations: (_: string, callback: UpdateLintingCallback) => {
this.updateLintingCallback = callback;
},
async: true,
lintOnChange: false,
},
};

if (!this.props.input.onChange || this.props.disabled) {
Expand Down Expand Up @@ -434,6 +451,28 @@ class CodeEditor extends Component<Props, State> {
}
};

lintCode() {
const { dataTreePath, dynamicData } = this.props;

if (!dataTreePath || !this.updateLintingCallback) {
return;
}

const errors = _.get(
dynamicData,
getEvalErrorPath(dataTreePath),
[],
) as EvaluationError[];

let annotations: Annotation[] = [];

if (this.editor) {
annotations = getLintAnnotations(this.editor.getValue(), errors);
}

this.updateLintingCallback(this.editor, annotations);
}

static updateMarkings = (
editor: CodeMirror.Editor,
marking: Array<MarkHelper>,
Expand Down Expand Up @@ -481,9 +520,11 @@ class CodeEditor extends Component<Props, State> {
getEvalErrorPath(dataTreePath),
[],
) as EvaluationError[];

const filteredLintErrors = errors.filter(
(error) => error.errorType !== PropertyEvaluationErrorType.LINT,
);

const pathEvaluatedValue = _.get(dataTree, getEvalValuePath(dataTreePath));

return {
Expand Down Expand Up @@ -524,6 +565,10 @@ class CodeEditor extends Component<Props, State> {
evaluated = pathEvaluatedValue;
}

if (getFeatureFlags().LINTING) {
this.lintCode();
}

const showEvaluatedValue =
this.state.isFocused &&
("evaluatedValue" in this.props ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { Severity } from "entities/AppsmithConsole";
import {
EvaluationError,
PropertyEvaluationErrorType,
} from "utils/DynamicBindingUtils";
import {
getKeyPositionInString,
getLintAnnotations,
getAllWordOccurrences,
} from "./lintHelpers";

describe("getAllWordOccurences()", function() {
it("should get all the indexes", () => {
const res = getAllWordOccurrences("this is a `this` string", "this");
expect(res).toEqual([0, 11]);
});

it("should return empty array", () => {
expect(getAllWordOccurrences("this is a string", "number")).toEqual([]);
});
});

describe("getKeyPositionsInString()", () => {
it("should return results for single line string", () => {
const res = getKeyPositionInString("this is a `this` string", "this");
expect(res).toEqual([
{ line: 0, ch: 0 },
{ line: 0, ch: 11 },
]);
});

it("should return results for multiline string", () => {
const res = getKeyPositionInString("this is a \n`this` string", "this");
expect(res).toEqual([
{ line: 0, ch: 0 },
{ line: 1, ch: 1 },
]);
});
});

describe("getLintAnnotations()", () => {
const { LINT, PARSE } = PropertyEvaluationErrorType;
const { ERROR, WARNING } = Severity;
it("should return proper annotations", () => {
const value = `Hello {{ world == test }}\n {{text}}`;
const errors: EvaluationError[] = [
{
errorMessage: "Expected '===' and instead saw '=='.",
severity: WARNING,
raw:
"\n function closedFunction () {\n const result = world == test \n return result;\n }\n closedFunction()\n ",
errorType: LINT,
originalBinding: "world == test ",
errorSegment: " const result = world == test ",
variables: ["===", "==", null, null],
code: "W116",
},
{
errorType: LINT,
raw:
"\n function closedFunction () {\n const result = world == test \n return result;\n }\n closedFunction()\n ",
severity: WARNING,
errorMessage: "'world' is not defined.",
errorSegment: " const result = world == test ",
originalBinding: "world == test ",
variables: ["world", null, null, null],
code: "W117",
},
{
errorType: LINT,
raw:
"\n function closedFunction () {\n const result = world == test \n return result;\n }\n closedFunction()\n ",
severity: WARNING,
errorMessage: "'test' is not defined.",
errorSegment: " const result = world == test ",
originalBinding: "world == test ",
variables: ["test", null, null, null],
code: "W117",
},
{
errorType: PARSE,
raw:
"\n function closedFunction () {\n const result = world == test \n return result;\n }\n closedFunction()\n ",
severity: ERROR,
errorMessage: "ReferenceError: world is not defined",
originalBinding: " world == test ",
},
{
errorMessage: "'text' is not defined.",
severity: WARNING,
raw:
"\n function closedFunction () {\n const result = text\n return result;\n }\n closedFunction()\n ",
errorType: LINT,
originalBinding: "text",
errorSegment: " const result = text",
variables: ["text", null, null, null],
code: "W117",
},
{
errorMessage: "ReferenceError: text is not defined",
severity: WARNING,
raw:
"\n function closedFunction () {\n const result = text\n return result;\n }\n closedFunction()\n ",
errorType: PARSE,
originalBinding: "text",
},
];

const res = getLintAnnotations(value, errors);
expect(res).toEqual([
{
from: {
line: 0,
ch: 15,
},
to: {
line: 0,
ch: 17,
},
message: "Expected '===' and instead saw '=='.",
severity: "warning",
},
{
from: {
line: 0,
ch: 9,
},
to: {
line: 0,
ch: 14,
},
message: "'world' is not defined.",
severity: "warning",
},
{
from: {
line: 0,
ch: 18,
},
to: {
line: 0,
ch: 22,
},
message: "'test' is not defined.",
severity: "warning",
},
{
from: {
line: 1,
ch: 4,
},
to: {
line: 1,
ch: 8,
},
message: "'text' is not defined.",
severity: "warning",
},
]);
});
});
Loading