chore: Modify logic to detect and hide deep paths in autocomplete suggestions (#33966)

## Description
This PR modifies how the AutocompleteSortRules suppresses paths with
more than 2 level of nesting.

Example: `Table1.selectedRow.address`
A path like the above should be suppressed from autocomplete suggestion
but it should not suppress `.run` of a module instance with autocomplete
params

Example `QueryModule1.run({ limit: 10, user: appsmith.user.name })`
With the above completion, the AutocompleteSortRules's
NoDeepNestedSuggestionsRule check for the `text` property of the
completion and checked agains the value shared in the above example.
This resulted in suppression of the `QueryModule.run` for the
autocomplete list. Therefore the logic now shift from check the `text`
property which is what gets replaced on selection to `displayText` which
is what we see in the autocomplete suggestion.

Fixes https://github.com/appsmithorg/appsmith/issues/33959

## Automation

/ok-to-test tags="@tag.All"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/9378147789>
> Commit: 62c4d96c18b2ed931dbe9d671f1eeaaa5c989059
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9378147789&attempt=3"
target="_blank">Click here!</a>

<!-- end of auto-generated comment: Cypress test results  -->













## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced autocomplete functionality by considering `displayText` for
more accurate suggestions.
- Improved code completion in the test suite by adding a new parameter
`name` with a default value referencing `appsmith.user.name`.

- **Tests**
- Updated test cases to reflect new autocomplete and code completion
features, including new declarations for `fieldEntityInformation`.

- **Refactor**
- Updated internal logic to handle new properties and ensure consistency
across the application.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
ashit-rath 2024-06-06 17:38:40 +05:30 committed by GitHub
parent 33b3949445
commit 1e8a962b8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 56 additions and 19 deletions

View File

@ -32,6 +32,7 @@ export const getShowHintOptions = (
data: {}, data: {},
text: "", text: "",
shortcut: "", shortcut: "",
displayText: "",
}; };
const cursor = editor.getCursor(); const cursor = editor.getCursor();
return { return {

View File

@ -130,7 +130,9 @@ class NoDeepNestedSuggestionsRule implements AutocompleteRule {
static threshold = -Infinity; static threshold = -Infinity;
computeScore(completion: Completion<TernCompletionResult>): number { computeScore(completion: Completion<TernCompletionResult>): number {
let score = 0; let score = 0;
if (completion.text.split(".").length > 2) const text = completion.displayText || "";
if (text.split(".").length > 2)
score = NoDeepNestedSuggestionsRule.threshold; score = NoDeepNestedSuggestionsRule.threshold;
return score; return score;
} }

View File

@ -26,11 +26,13 @@ const bigDoc = 250;
const cls = "CodeMirror-Tern-"; const cls = "CodeMirror-Tern-";
const hintDelay = 1700; const hintDelay = 1700;
type MakeRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
export interface Completion< export interface Completion<
T = { T = {
doc: string; doc: string;
}, },
> extends Hint { > extends MakeRequired<Hint, "displayText"> {
origin: string; origin: string;
type: AutocompleteDataType | string; type: AutocompleteDataType | string;
data: T; data: T;

View File

@ -14,6 +14,7 @@ import { AutocompleteSorter, ScoredCompletion } from "../AutocompleteSortRules";
import type CodeMirror from "codemirror"; import type CodeMirror from "codemirror";
import type { Def } from "tern"; import type { Def } from "tern";
import type { Doc } from "codemirror"; import type { Doc } from "codemirror";
import type { FieldEntityInformation } from "components/editorComponents/CodeEditor/EditorConfig";
jest.mock("utils/getCodeMirrorNamespace", () => { jest.mock("utils/getCodeMirrorNamespace", () => {
const actual = jest.requireActual("utils/getCodeMirrorNamespace"); const actual = jest.requireActual("utils/getCodeMirrorNamespace");
@ -243,6 +244,7 @@ describe("Tern server sorting", () => {
new Map(); new Map();
const contextCompletion: Completion = { const contextCompletion: Completion = {
text: "context", text: "context",
displayText: "context",
type: AutocompleteDataType.STRING, type: AutocompleteDataType.STRING,
origin: "[doc]", origin: "[doc]",
data: { data: {
@ -252,6 +254,7 @@ describe("Tern server sorting", () => {
const sameEntityCompletion: Completion<any> = { const sameEntityCompletion: Completion<any> = {
text: "sameEntity.tableData", text: "sameEntity.tableData",
displayText: "sameEntity.tableData",
type: AutocompleteDataType.ARRAY, type: AutocompleteDataType.ARRAY,
origin: "DATA_TREE", origin: "DATA_TREE",
data: {}, data: {},
@ -267,6 +270,7 @@ describe("Tern server sorting", () => {
const priorityCompletion: Completion<any> = { const priorityCompletion: Completion<any> = {
text: "selectedRow", text: "selectedRow",
displayText: "selectedRow",
type: AutocompleteDataType.OBJECT, type: AutocompleteDataType.OBJECT,
origin: "DATA_TREE", origin: "DATA_TREE",
data: {}, data: {},
@ -282,6 +286,7 @@ describe("Tern server sorting", () => {
const diffTypeCompletion: Completion<any> = { const diffTypeCompletion: Completion<any> = {
text: "diffType.tableData", text: "diffType.tableData",
displayText: "diffType.tableData",
type: AutocompleteDataType.ARRAY, type: AutocompleteDataType.ARRAY,
origin: "DATA_TREE.WIDGET", origin: "DATA_TREE.WIDGET",
data: {}, data: {},
@ -298,6 +303,7 @@ describe("Tern server sorting", () => {
const sameTypeDiffEntityTypeCompletion: Completion<any> = { const sameTypeDiffEntityTypeCompletion: Completion<any> = {
text: "diffEntity.data", text: "diffEntity.data",
displayText: "diffEntity.data",
type: AutocompleteDataType.OBJECT, type: AutocompleteDataType.OBJECT,
origin: "DATA_TREE", origin: "DATA_TREE",
data: {}, data: {},
@ -310,6 +316,7 @@ describe("Tern server sorting", () => {
const dataTreeCompletion: Completion<any> = { const dataTreeCompletion: Completion<any> = {
text: "otherDataTree", text: "otherDataTree",
displayText: "otherDataTree",
type: AutocompleteDataType.STRING, type: AutocompleteDataType.STRING,
origin: "DATA_TREE", origin: "DATA_TREE",
data: {}, data: {},
@ -322,6 +329,7 @@ describe("Tern server sorting", () => {
const functionCompletion: Completion<any> = { const functionCompletion: Completion<any> = {
text: "otherDataFunction", text: "otherDataFunction",
displayText: "otherDataFunction",
type: AutocompleteDataType.FUNCTION, type: AutocompleteDataType.FUNCTION,
origin: "DATA_TREE.APPSMITH.FUNCTIONS", origin: "DATA_TREE.APPSMITH.FUNCTIONS",
data: {}, data: {},
@ -329,6 +337,7 @@ describe("Tern server sorting", () => {
const ecmascriptCompletion: Completion<any> = { const ecmascriptCompletion: Completion<any> = {
text: "otherJS", text: "otherJS",
displayText: "otherJS",
type: AutocompleteDataType.OBJECT, type: AutocompleteDataType.OBJECT,
origin: "ecmascript", origin: "ecmascript",
data: {}, data: {},
@ -336,6 +345,7 @@ describe("Tern server sorting", () => {
const libCompletion: Completion<any> = { const libCompletion: Completion<any> = {
text: "libValue", text: "libValue",
displayText: "libValue",
type: AutocompleteDataType.OBJECT, type: AutocompleteDataType.OBJECT,
origin: "LIB/lodash", origin: "LIB/lodash",
data: {}, data: {},
@ -343,6 +353,7 @@ describe("Tern server sorting", () => {
const unknownCompletion: Completion<any> = { const unknownCompletion: Completion<any> = {
text: "unknownSuggestion", text: "unknownSuggestion",
displayText: "unknownSuggestion",
type: AutocompleteDataType.UNKNOWN, type: AutocompleteDataType.UNKNOWN,
origin: "unknown", origin: "unknown",
data: {}, data: {},
@ -445,8 +456,10 @@ describe("Tern server completion", () => {
"!type": "?", "!type": "?",
}, },
run: { run: {
"!type": "fn(inputs: {gender: any, limit: any}) -> +Promise", "!type":
"!fnParams": '{ gender: "male", limit: "5" }', "fn(inputs: {gender: any, limit: any, name: any }) -> +Promise",
"!fnParams":
'{ gender: "male", limit: "5", name: "Mr. " + appsmith.user.name }',
"!url": "!url":
"https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun",
"!doc": "Executes the query with the given input values.", "!doc": "Executes the query with the given input values.",
@ -461,7 +474,8 @@ describe("Tern server completion", () => {
}, },
"QueryModule11.run": { "QueryModule11.run": {
"!type": "fn(inputs: {gender: any, limit: any}) -> +Promise", "!type": "fn(inputs: {gender: any, limit: any}) -> +Promise",
"!fnParams": '{ gender: "male", limit: "5" }', "!fnParams":
'{ gender: "male", limit: "5", name: "Mr. " + appsmith.user.name }',
"!url": "!url":
"https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun",
"!doc": "Executes the query with the given input values.", "!doc": "Executes the query with the given input values.",
@ -496,7 +510,7 @@ describe("Tern server completion", () => {
}, },
{ {
name: "QueryModule11.run", name: "QueryModule11.run",
type: "fn(inputs: {gender: ?, limit: ?}) -> Promise", type: "fn(inputs: {gender: ?, limit: ?, name: ?}) -> Promise",
doc: "Executes the query with the given input values.", doc: "Executes the query with the given input values.",
url: "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", url: "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun",
origin: "DATA_TREE", origin: "DATA_TREE",
@ -542,12 +556,12 @@ describe("Tern server completion", () => {
isEntityName: true, isEntityName: true,
}, },
{ {
text: 'QueryModule11.run({ gender: "male", limit: "5" })', text: 'QueryModule11.run({ gender: "male", limit: "5", name: "Mr. " + appsmith.user.name })',
displayText: "QueryModule11.run", displayText: "QueryModule11.run",
className: "CodeMirror-Tern-completion CodeMirror-Tern-completion-fn", className: "CodeMirror-Tern-completion CodeMirror-Tern-completion-fn",
data: { data: {
name: "QueryModule11.run", name: "QueryModule11.run",
type: "fn(inputs: {gender: ?, limit: ?}) -> Promise", type: "fn(inputs: {gender: ?, limit: ?, name: ?}) -> Promise",
doc: "Executes the query with the given input values.", doc: "Executes the query with the given input values.",
url: "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", url: "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun",
origin: "DATA_TREE", origin: "DATA_TREE",
@ -560,13 +574,7 @@ describe("Tern server completion", () => {
}, },
]; ];
// The current cursor location that is being written in the code mirror editor const mockToken = {
MockCodemirrorEditor.getCursor.mockResolvedValue({
line: 10,
ch: 30,
sticky: null,
});
MockCodemirrorEditor.getTokenAt.mockResolvedValue({
start: 22, start: 22,
end: 30, end: 30,
string: "QueryMod", string: "QueryMod",
@ -639,8 +647,35 @@ describe("Tern server completion", () => {
}, },
indented: 4, indented: 4,
}, },
}); };
const fieldEntityInformation = {
mode: "javascript",
isTriggerPath: true,
entityName: "JSObject1",
propertyPath: "body",
entityType: "JSACTION",
blockCompletions: [
{
parentPath: "this",
subPath: "myFun2()",
},
{
parentPath: "JSObject1",
subPath: "myFun2()",
},
],
token: mockToken,
} as FieldEntityInformation;
// The current cursor location that is being written in the code mirror editor
MockCodemirrorEditor.getCursor.mockResolvedValue({
line: 10,
ch: 30,
sticky: null,
});
MockCodemirrorEditor.getTokenAt.mockResolvedValue(mockToken);
CodemirrorTernService.fieldEntityInformation = fieldEntityInformation;
CodemirrorTernService.entityDef = entityDef; CodemirrorTernService.entityDef = entityDef;
// The current line that is being written in the code mirror editor // The current line that is being written in the code mirror editor
@ -662,9 +697,6 @@ describe("Tern server completion", () => {
name: "", name: "",
changed: null, changed: null,
}); });
jest
.spyOn(AutocompleteSorter, "sort")
.mockImplementation((completions) => completions);
CodemirrorTernService.defEntityInformation = new Map([ CodemirrorTernService.defEntityInformation = new Map([
[ [