From 1e8a962b8bb0c4c9ec6852d73823cd2bbe72926c Mon Sep 17 00:00:00 2001 From: ashit-rath Date: Thu, 6 Jun 2024 17:38:40 +0530 Subject: [PATCH] chore: Modify logic to detect and hide deep paths in autocomplete suggestions (#33966) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: 62c4d96c18b2ed931dbe9d671f1eeaaa5c989059 > Cypress dashboard url: Click here! ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## 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. --- .../CodeEditor/commandsHelper.ts | 1 + .../autocomplete/AutocompleteSortRules.ts | 4 +- .../autocomplete/CodemirrorTernService.ts | 4 +- .../autocomplete/__tests__/TernServer.test.ts | 66 ++++++++++++++----- 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/app/client/src/components/editorComponents/CodeEditor/commandsHelper.ts b/app/client/src/components/editorComponents/CodeEditor/commandsHelper.ts index 1741bfa5ba..ba62ec3fb9 100644 --- a/app/client/src/components/editorComponents/CodeEditor/commandsHelper.ts +++ b/app/client/src/components/editorComponents/CodeEditor/commandsHelper.ts @@ -32,6 +32,7 @@ export const getShowHintOptions = ( data: {}, text: "", shortcut: "", + displayText: "", }; const cursor = editor.getCursor(); return { diff --git a/app/client/src/utils/autocomplete/AutocompleteSortRules.ts b/app/client/src/utils/autocomplete/AutocompleteSortRules.ts index b8801116aa..67db352704 100644 --- a/app/client/src/utils/autocomplete/AutocompleteSortRules.ts +++ b/app/client/src/utils/autocomplete/AutocompleteSortRules.ts @@ -130,7 +130,9 @@ class NoDeepNestedSuggestionsRule implements AutocompleteRule { static threshold = -Infinity; computeScore(completion: Completion): number { let score = 0; - if (completion.text.split(".").length > 2) + const text = completion.displayText || ""; + + if (text.split(".").length > 2) score = NoDeepNestedSuggestionsRule.threshold; return score; } diff --git a/app/client/src/utils/autocomplete/CodemirrorTernService.ts b/app/client/src/utils/autocomplete/CodemirrorTernService.ts index 63b8b037a4..f9b9a1cfbf 100644 --- a/app/client/src/utils/autocomplete/CodemirrorTernService.ts +++ b/app/client/src/utils/autocomplete/CodemirrorTernService.ts @@ -26,11 +26,13 @@ const bigDoc = 250; const cls = "CodeMirror-Tern-"; const hintDelay = 1700; +type MakeRequired = Omit & Required>; + export interface Completion< T = { doc: string; }, -> extends Hint { +> extends MakeRequired { origin: string; type: AutocompleteDataType | string; data: T; diff --git a/app/client/src/utils/autocomplete/__tests__/TernServer.test.ts b/app/client/src/utils/autocomplete/__tests__/TernServer.test.ts index 5f240c1973..858f60af72 100644 --- a/app/client/src/utils/autocomplete/__tests__/TernServer.test.ts +++ b/app/client/src/utils/autocomplete/__tests__/TernServer.test.ts @@ -14,6 +14,7 @@ import { AutocompleteSorter, ScoredCompletion } from "../AutocompleteSortRules"; import type CodeMirror from "codemirror"; import type { Def } from "tern"; import type { Doc } from "codemirror"; +import type { FieldEntityInformation } from "components/editorComponents/CodeEditor/EditorConfig"; jest.mock("utils/getCodeMirrorNamespace", () => { const actual = jest.requireActual("utils/getCodeMirrorNamespace"); @@ -243,6 +244,7 @@ describe("Tern server sorting", () => { new Map(); const contextCompletion: Completion = { text: "context", + displayText: "context", type: AutocompleteDataType.STRING, origin: "[doc]", data: { @@ -252,6 +254,7 @@ describe("Tern server sorting", () => { const sameEntityCompletion: Completion = { text: "sameEntity.tableData", + displayText: "sameEntity.tableData", type: AutocompleteDataType.ARRAY, origin: "DATA_TREE", data: {}, @@ -267,6 +270,7 @@ describe("Tern server sorting", () => { const priorityCompletion: Completion = { text: "selectedRow", + displayText: "selectedRow", type: AutocompleteDataType.OBJECT, origin: "DATA_TREE", data: {}, @@ -282,6 +286,7 @@ describe("Tern server sorting", () => { const diffTypeCompletion: Completion = { text: "diffType.tableData", + displayText: "diffType.tableData", type: AutocompleteDataType.ARRAY, origin: "DATA_TREE.WIDGET", data: {}, @@ -298,6 +303,7 @@ describe("Tern server sorting", () => { const sameTypeDiffEntityTypeCompletion: Completion = { text: "diffEntity.data", + displayText: "diffEntity.data", type: AutocompleteDataType.OBJECT, origin: "DATA_TREE", data: {}, @@ -310,6 +316,7 @@ describe("Tern server sorting", () => { const dataTreeCompletion: Completion = { text: "otherDataTree", + displayText: "otherDataTree", type: AutocompleteDataType.STRING, origin: "DATA_TREE", data: {}, @@ -322,6 +329,7 @@ describe("Tern server sorting", () => { const functionCompletion: Completion = { text: "otherDataFunction", + displayText: "otherDataFunction", type: AutocompleteDataType.FUNCTION, origin: "DATA_TREE.APPSMITH.FUNCTIONS", data: {}, @@ -329,6 +337,7 @@ describe("Tern server sorting", () => { const ecmascriptCompletion: Completion = { text: "otherJS", + displayText: "otherJS", type: AutocompleteDataType.OBJECT, origin: "ecmascript", data: {}, @@ -336,6 +345,7 @@ describe("Tern server sorting", () => { const libCompletion: Completion = { text: "libValue", + displayText: "libValue", type: AutocompleteDataType.OBJECT, origin: "LIB/lodash", data: {}, @@ -343,6 +353,7 @@ describe("Tern server sorting", () => { const unknownCompletion: Completion = { text: "unknownSuggestion", + displayText: "unknownSuggestion", type: AutocompleteDataType.UNKNOWN, origin: "unknown", data: {}, @@ -445,8 +456,10 @@ describe("Tern server completion", () => { "!type": "?", }, run: { - "!type": "fn(inputs: {gender: any, limit: any}) -> +Promise", - "!fnParams": '{ gender: "male", limit: "5" }', + "!type": + "fn(inputs: {gender: any, limit: any, name: any }) -> +Promise", + "!fnParams": + '{ gender: "male", limit: "5", name: "Mr. " + appsmith.user.name }', "!url": "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", "!doc": "Executes the query with the given input values.", @@ -461,7 +474,8 @@ describe("Tern server completion", () => { }, "QueryModule11.run": { "!type": "fn(inputs: {gender: any, limit: any}) -> +Promise", - "!fnParams": '{ gender: "male", limit: "5" }', + "!fnParams": + '{ gender: "male", limit: "5", name: "Mr. " + appsmith.user.name }', "!url": "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", "!doc": "Executes the query with the given input values.", @@ -496,7 +510,7 @@ describe("Tern server completion", () => { }, { 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.", url: "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", origin: "DATA_TREE", @@ -542,12 +556,12 @@ describe("Tern server completion", () => { isEntityName: true, }, { - text: 'QueryModule11.run({ gender: "male", limit: "5" })', + text: 'QueryModule11.run({ gender: "male", limit: "5", name: "Mr. " + appsmith.user.name })', displayText: "QueryModule11.run", className: "CodeMirror-Tern-completion CodeMirror-Tern-completion-fn", data: { 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.", url: "https://docs.appsmith.com/reference/appsmith-framework/query-object#queryrun", origin: "DATA_TREE", @@ -560,13 +574,7 @@ describe("Tern server completion", () => { }, ]; - // 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({ + const mockToken = { start: 22, end: 30, string: "QueryMod", @@ -639,8 +647,35 @@ describe("Tern server completion", () => { }, 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; // The current line that is being written in the code mirror editor @@ -662,9 +697,6 @@ describe("Tern server completion", () => { name: "", changed: null, }); - jest - .spyOn(AutocompleteSorter, "sort") - .mockImplementation((completions) => completions); CodemirrorTernService.defEntityInformation = new Map([ [