Merge branch 'fix/autocomplete-bugs' into 'release'

Fix autocomplete visibility bugs

See merge request theappsmith/internal-tools-client!674
This commit is contained in:
Hetu Nandu 2020-06-03 17:27:24 +00:00
commit 086f9559a9
3 changed files with 45 additions and 30 deletions

View File

@ -17,7 +17,7 @@ import ErrorTooltip from "components/editorComponents/ErrorTooltip";
import HelperTooltip from "components/editorComponents/HelperTooltip";
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
import _ from "lodash";
import { parseDynamicString } from "utils/DynamicBindingUtils";
import { getDynamicStringSegments } from "utils/DynamicBindingUtils";
import { DataTree } from "entities/DataTree/dataTreeFactory";
import { Theme } from "constants/DefaultTheme";
import AnalyticsUtil from "utils/AnalyticsUtil";
@ -324,6 +324,7 @@ class DynamicAutocompleteInput extends Component<Props, State> {
});
this.editor.on("change", _.debounce(this.handleChange, 300));
this.editor.on("change", this.handleAutocompleteVisibility);
this.editor.on("keyup", this.handleAutocompleteHide);
this.editor.on("focus", this.handleEditorFocus);
this.editor.on("blur", this.handleEditorBlur);
@ -409,7 +410,6 @@ class DynamicAutocompleteInput extends Component<Props, State> {
globalScope: this.props.dynamicData,
});
}
this.editor.on("cursorActivity", this.handleAutocompleteVisibility);
}
handleEditorFocus = () => {
@ -447,20 +447,30 @@ class DynamicAutocompleteInput extends Component<Props, State> {
let cursorBetweenBinding = false;
const cursor = this.editor.getCursor();
const value = this.editor.getValue();
let cursorIndex = cursor.ch;
if (cursor.line > 0) {
for (let lineIndex = 0; lineIndex < cursor.line; lineIndex++) {
const line = this.editor.getLine(lineIndex);
// Add line length + 1 for new line character
cursorIndex = cursorIndex + line.length + 1;
}
}
const stringSegments = getDynamicStringSegments(value);
// count of chars processed
let cumulativeCharCount = 0;
parseDynamicString(value).forEach(segment => {
stringSegments.forEach((segment: string) => {
const start = cumulativeCharCount;
const dynamicStart = segment.indexOf("{{");
const dynamicDoesStart = dynamicStart > -1;
const dynamicEnd = segment.indexOf("}}");
const dynamicDoesEnd = dynamicEnd > -1;
const dynamicStartIndex = dynamicStart + start + 1;
const dynamicEndIndex = dynamicEnd + start + 1;
const dynamicStartIndex = dynamicStart + start + 2;
const dynamicEndIndex = dynamicEnd + start;
if (
dynamicDoesStart &&
cursor.ch > dynamicStartIndex &&
((dynamicDoesEnd && cursor.ch < dynamicEndIndex) ||
(!dynamicDoesEnd && cursor.ch > dynamicStartIndex))
cursorIndex >= dynamicStartIndex &&
((dynamicDoesEnd && cursorIndex <= dynamicEndIndex) ||
(!dynamicDoesEnd && cursorIndex >= dynamicStartIndex))
) {
cursorBetweenBinding = true;
}

View File

@ -31,15 +31,15 @@ export const isDynamicValue = (value: string): boolean =>
DATA_BIND_REGEX.test(value);
//{{}}{{}}}
export function parseDynamicString(dynamicString: string): string[] {
let parsedDynamicValues = [];
export function getDynamicStringSegments(dynamicString: string): string[] {
let stringSegments = [];
const indexOfDoubleParanStart = dynamicString.indexOf("{{");
if (indexOfDoubleParanStart === -1) {
return [dynamicString];
}
//{{}}{{}}}
const firstString = dynamicString.substring(0, indexOfDoubleParanStart);
firstString && parsedDynamicValues.push(firstString);
firstString && stringSegments.push(firstString);
let rest = dynamicString.substring(
indexOfDoubleParanStart,
dynamicString.length,
@ -55,11 +55,11 @@ export function parseDynamicString(dynamicString: string): string[] {
} else if (char === "}") {
sum--;
if (prevChar === "}" && sum === 0) {
parsedDynamicValues.push(rest.substring(0, i + 1));
stringSegments.push(rest.substring(0, i + 1));
rest = rest.substring(i + 1, rest.length);
if (rest) {
parsedDynamicValues = parsedDynamicValues.concat(
parseDynamicString(rest),
stringSegments = stringSegments.concat(
getDynamicStringSegments(rest),
);
break;
}
@ -69,7 +69,7 @@ export function parseDynamicString(dynamicString: string): string[] {
if (sum !== 0 && dynamicString !== "") {
return [dynamicString];
}
return parsedDynamicValues;
return stringSegments;
}
const getAllPaths = (
@ -95,24 +95,24 @@ const getAllPaths = (
export const getDynamicBindings = (
dynamicString: string,
): { mustaches: string[]; jsSnippets: string[] } => {
): { stringSegments: string[]; jsSnippets: string[] } => {
// Protect against bad string parse
if (!dynamicString || !_.isString(dynamicString)) {
return { mustaches: [], jsSnippets: [] };
return { stringSegments: [], jsSnippets: [] };
}
const sanitisedString = dynamicString.trim();
// Get the {{binding}} bound values
const bindings = parseDynamicString(sanitisedString);
const stringSegments = getDynamicStringSegments(sanitisedString);
// Get the "binding" path values
const paths = bindings.map(binding => {
const length = binding.length;
const matches = isDynamicValue(binding);
const paths = stringSegments.map(segment => {
const length = segment.length;
const matches = isDynamicValue(segment);
if (matches) {
return binding.substring(2, length - 2);
return segment.substring(2, length - 2);
}
return "";
});
return { mustaches: bindings, jsSnippets: paths };
return { stringSegments: stringSegments, jsSnippets: paths };
};
// Paths are expected to have "{name}.{path}" signature
@ -162,8 +162,10 @@ export const getDynamicValue = (
includeTriggers = false,
): JSExecutorResult => {
// Get the {{binding}} bound values
const { mustaches, jsSnippets } = getDynamicBindings(dynamicBinding);
if (mustaches.length) {
const { stringSegments: stringSegments, jsSnippets } = getDynamicBindings(
dynamicBinding,
);
if (stringSegments.length) {
// Get the Data Tree value of those "binding "paths
const values = jsSnippets.map((jsSnippet, index) => {
if (jsSnippet) {
@ -174,16 +176,16 @@ export const getDynamicValue = (
return { result: result.result };
}
} else {
return { result: mustaches[index], triggers: [] };
return { result: stringSegments[index], triggers: [] };
}
});
// if it is just one binding, no need to create template string
if (mustaches.length === 1) return values[0];
if (stringSegments.length === 1) return values[0];
// else return a string template with bindings
const templateString = createDynamicValueString(
dynamicBinding,
mustaches,
stringSegments,
values.map(v => v.result),
);
return {

View File

@ -137,8 +137,11 @@ class TernServer {
.sort((a, b) => {
return a.text.toLowerCase().localeCompare(b.text.toLowerCase());
});
const otherCompletions = completions.filter(c => c.origin !== "dataTree");
return [...dataTreeCompletions, ...otherCompletions];
const docCompletetions = completions.filter(c => c.origin === "[doc]");
const otherCompletions = completions.filter(
c => c.origin !== "dataTree" && c.origin !== "[doc]",
);
return [...docCompletetions, ...dataTreeCompletions, ...otherCompletions];
}
typeToIcon(type: string) {