export function FormulaEditor()

in x-pack/platform/plugins/shared/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx [96:964]


export function FormulaEditor({
  layer,
  paramEditorUpdater,
  currentColumn,
  columnId,
  indexPattern,
  operationDefinitionMap,
  unifiedSearch,
  dataViews,
  toggleFullscreen,
  isFullscreen,
  dateHistogramInterval,
  hasData,
  dateRange,
  uiSettings,
  data,
}: Omit<ParamEditorProps<FormulaIndexPatternColumn>, 'activeData'> & {
  dateHistogramInterval: ReturnType<typeof getDateHistogramInterval>;
  hasData: boolean;
}) {
  const [text, setText] = useState(currentColumn.params.formula);
  const [warnings, setWarnings] = useState<
    Array<{ severity: monaco.MarkerSeverity; message: string }>
  >([]);
  const [isHelpOpen, setIsHelpOpen] = useState<boolean>(isFullscreen);
  const [isWarningOpen, setIsWarningOpen] = useState<boolean>(false);
  const [isWordWrapped, toggleWordWrap] = useState<boolean>(true);
  const editorModel = React.useRef<monaco.editor.ITextModel>();
  const overflowDiv1 = React.useRef<HTMLElement>();
  const disposables = React.useRef<monaco.IDisposable[]>([]);
  const editor1 = React.useRef<monaco.editor.IStandaloneCodeEditor>();

  const euiThemeContext = useEuiTheme();
  const { euiTheme } = euiThemeContext;

  const visibleOperationsMap = useMemo(
    () => filterByVisibleOperation(operationDefinitionMap),
    [operationDefinitionMap]
  );

  const documentationSections = useMemo(
    () =>
      getDocumentationSections({
        indexPattern,
        operationDefinitionMap: visibleOperationsMap,
      }),
    [indexPattern, visibleOperationsMap]
  );

  const baseInterval =
    'interval' in dateHistogramInterval
      ? dateHistogramInterval.interval?.asMilliseconds()
      : undefined;
  const baseIntervalRef = useRef(baseInterval);
  baseIntervalRef.current = baseInterval;

  // The Monaco editor needs to have the overflowDiv in the first render. Using an effect
  // requires a second render to work, so we are using an if statement to guarantee it happens
  // on first render
  if (!overflowDiv1?.current) {
    const node1 = (overflowDiv1.current = document.createElement('div'));
    node1.setAttribute('data-test-subj', 'lnsFormulaWidget');
    // Monaco CSS is targeted on the monaco-editor class
    node1.classList.add('lnsFormulaOverflow', 'monaco-editor');
    document.body.appendChild(node1);
  }

  // Clean up the monaco editor and DOM on unmount
  useEffect(() => {
    const model = editorModel;
    const allDisposables = disposables;
    const editor1ref = editor1;
    return () => {
      model.current?.dispose();
      overflowDiv1.current?.parentNode?.removeChild(overflowDiv1.current);
      editor1ref.current?.dispose();
      allDisposables.current?.forEach((d) => d.dispose());
    };
  }, []);

  useUnmount(() => {
    // If the text is not synced, update the column.
    if (text !== currentColumn.params.formula) {
      paramEditorUpdater(
        (prevLayer) =>
          insertOrReplaceFormulaColumn(
            columnId,
            {
              ...currentColumn,
              params: {
                ...currentColumn.params,
                formula: text || '',
              },
            },
            prevLayer,
            {
              indexPattern,
              operations: operationDefinitionMap,
              dateRange,
            }
          ).layer
      );
    }
  });

  useDebounceWithOptions(
    () => {
      if (!editorModel.current) return;

      if (!text) {
        setWarnings([]);
        monaco.editor.setModelMarkers(editorModel.current, 'LENS', []);
        if (currentColumn.params.formula) {
          // Only submit if valid
          paramEditorUpdater(
            insertOrReplaceFormulaColumn(
              columnId,
              {
                ...currentColumn,
                params: {
                  ...currentColumn.params,
                  formula: text || '',
                },
              },
              layer,
              {
                indexPattern,
                operations: operationDefinitionMap,
                dateRange,
              }
            ).layer
          );
        }

        return;
      }

      let errors: ErrorWrapper[] = [];

      const parseResponse = tryToParse(text, visibleOperationsMap);
      if ('error' in parseResponse) {
        errors = [parseResponse.error];
      } else {
        const validationErrors = runASTValidation(
          parseResponse.root,
          layer,
          indexPattern,
          visibleOperationsMap,
          currentColumn,
          dateRange
        );
        if (validationErrors.length) {
          errors = validationErrors;
        }
      }

      if (errors.length) {
        // Replace the previous error with the new one
        const previousFormulaWasBroken = currentColumn.params.isFormulaBroken;
        // If the user is changing a previous formula and there are currently no result
        // show the most up-to-date state with the error message.
        const previousFormulaWasOkButNoData = !currentColumn.params.isFormulaBroken && !hasData;
        if (previousFormulaWasBroken || previousFormulaWasOkButNoData) {
          // If the formula is already broken, show the latest error message in the workspace
          if (currentColumn.params.formula !== text) {
            paramEditorUpdater(
              insertOrReplaceFormulaColumn(
                columnId,
                {
                  ...currentColumn,
                  params: {
                    ...currentColumn.params,
                    formula: text || '',
                  },
                },
                layer,
                {
                  indexPattern,
                  operations: operationDefinitionMap,
                  dateRange,
                }
              ).layer
            );
          }
        }

        const markers = errors.flatMap((innerError) => {
          if (innerError.locations.length) {
            return innerError.locations.map((location) => {
              const startPosition = offsetToRowColumn(text, location.min);
              const endPosition = offsetToRowColumn(text, location.max);
              return {
                message: innerError.message,
                startColumn: startPosition.column + 1,
                startLineNumber: startPosition.lineNumber,
                endColumn: endPosition.column + 1,
                endLineNumber: endPosition.lineNumber,
                severity:
                  innerError.severity === 'warning'
                    ? monaco.MarkerSeverity.Warning
                    : monaco.MarkerSeverity.Error,
              };
            });
          } else {
            // Parse errors return no location info
            const startPosition = offsetToRowColumn(text, 0);
            const endPosition = offsetToRowColumn(text, text.length - 1);
            return [
              {
                message: innerError.message,
                startColumn: startPosition.column + 1,
                startLineNumber: startPosition.lineNumber,
                endColumn: endPosition.column + 1,
                endLineNumber: endPosition.lineNumber,
                severity:
                  innerError.severity === 'warning'
                    ? monaco.MarkerSeverity.Warning
                    : monaco.MarkerSeverity.Error,
              },
            ];
          }
        });

        monaco.editor.setModelMarkers(editorModel.current, 'LENS', markers);
        setWarnings(markers.map(({ severity, message }) => ({ severity, message })));
      } else {
        monaco.editor.setModelMarkers(editorModel.current, 'LENS', []);

        // Only submit if valid
        const {
          layer: newLayer,
          meta: { locations },
        } = insertOrReplaceFormulaColumn(
          columnId,
          {
            ...currentColumn,
            params: {
              ...currentColumn.params,
              formula: text || '',
            },
          },
          layer,
          {
            indexPattern,
            operations: operationDefinitionMap,
            dateRange,
          }
        );

        paramEditorUpdater(newLayer);

        const managedColumns = getManagedColumnsFrom(columnId, newLayer.columns);
        const markers: monaco.editor.IMarkerData[] = managedColumns
          .flatMap(([id, column]) => {
            const newWarnings: monaco.editor.IMarkerData[] = [];
            if (locations[id]) {
              const def = visibleOperationsMap[column.operationType];
              if (def.getErrorMessage) {
                const messages = def.getErrorMessage(
                  newLayer,
                  id,
                  indexPattern,
                  dateRange,
                  visibleOperationsMap,
                  uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET)
                );
                if (messages.length) {
                  const startPosition = offsetToRowColumn(text, locations[id].min);
                  const endPosition = offsetToRowColumn(text, locations[id].max);
                  newWarnings.push({
                    message: messages.map((e) => e.message).join(', '),
                    startColumn: startPosition.column + 1,
                    startLineNumber: startPosition.lineNumber,
                    endColumn: endPosition.column + 1,
                    endLineNumber: endPosition.lineNumber,
                    severity: monaco.MarkerSeverity.Warning,
                  });
                }
              }
              if (def.shiftable && column.timeShift) {
                const startPosition = offsetToRowColumn(text, locations[id].min);
                const endPosition = offsetToRowColumn(text, locations[id].max);
                newWarnings.push(
                  ...getColumnTimeShiftWarnings(dateHistogramInterval, column.timeShift).map(
                    (message) => ({
                      message,
                      startColumn: startPosition.column + 1,
                      startLineNumber: startPosition.lineNumber,
                      endColumn: endPosition.column + 1,
                      endLineNumber: endPosition.lineNumber,
                      severity: monaco.MarkerSeverity.Warning,
                    })
                  )
                );
              }
            }
            return newWarnings;
          })
          .filter(nonNullable);
        setWarnings(markers.map(({ severity, message }) => ({ severity, message })));
        monaco.editor.setModelMarkers(editorModel.current, 'LENS', markers);
      }
    },
    // Make it validate on flyout open in case of a broken formula left over
    // from a previous edit
    { skipFirstRender: false },
    256,
    [text, currentColumn.filter]
  );

  const errorCount = warnings.filter(
    (marker) => marker.severity === monaco.MarkerSeverity.Error
  ).length;
  const warningCount = warnings.filter(
    (marker) => marker.severity === monaco.MarkerSeverity.Warning
  ).length;

  /**
   * The way that Monaco requests autocompletion is not intuitive, but the way we use it
   * we fetch new suggestions in these scenarios:
   *
   * - If the user types one of the trigger characters, suggestions are always fetched
   * - When the user selects the kql= suggestion, we tell Monaco to trigger new suggestions after
   * - When the user types the first character into an empty text box, Monaco requests suggestions
   *
   * Monaco also triggers suggestions automatically when there are no suggestions being displayed
   * and the user types a non-whitespace character.
   *
   * While suggestions are being displayed, Monaco uses an in-memory cache of the last known suggestions.
   */
  const provideCompletionItems = useCallback(
    async (
      model: monaco.editor.ITextModel,
      position: monaco.Position,
      context: monaco.languages.CompletionContext
    ) => {
      const innerText = model.getValue();
      let aSuggestions: LensMathSuggestions = {
        list: [],
        type: SUGGESTION_TYPE.FIELD,
      };
      const offset = monacoPositionToOffset(innerText, position);

      if (context.triggerCharacter === '(') {
        // Monaco usually inserts the end quote and reports the position is after the end quote
        if (innerText.slice(offset - 1, offset + 1) === '()') {
          position = position.delta(0, -1);
        }
        const wordUntil = model.getWordAtPosition(position.delta(0, -3));
        if (wordUntil) {
          // Retrieve suggestions for subexpressions
          aSuggestions = await suggest({
            expression: innerText,
            zeroIndexedOffset: offset,
            context,
            indexPattern,
            operationDefinitionMap: visibleOperationsMap,
            unifiedSearch,
            dataViews,
            dateHistogramInterval: baseIntervalRef.current,
            timefilter: data.query.timefilter.timefilter,
          });
        }
      } else {
        aSuggestions = await suggest({
          expression: innerText,
          zeroIndexedOffset: offset,
          context,
          indexPattern,
          operationDefinitionMap: visibleOperationsMap,
          unifiedSearch,
          dataViews,
          dateHistogramInterval: baseIntervalRef.current,
          timefilter: data.query.timefilter.timefilter,
        });
      }

      return {
        suggestions: aSuggestions.list.map((s) =>
          getSuggestion(
            s,
            aSuggestions.type,
            visibleOperationsMap,
            context.triggerCharacter,
            aSuggestions.range
          )
        ),
      };
    },
    [indexPattern, visibleOperationsMap, unifiedSearch, dataViews, data.query.timefilter.timefilter]
  );

  const provideSignatureHelp = useCallback(
    async (
      model: monaco.editor.ITextModel,
      position: monaco.Position,
      token: monaco.CancellationToken,
      context: monaco.languages.SignatureHelpContext
    ) => {
      const innerText = model.getValue();
      const textRange = model.getFullModelRange();

      const lengthAfterPosition = model.getValueLengthInRange({
        startLineNumber: position.lineNumber,
        startColumn: position.column,
        endLineNumber: textRange.endLineNumber,
        endColumn: textRange.endColumn,
      });
      return getSignatureHelp(
        model.getValue(),
        innerText.length - lengthAfterPosition,
        visibleOperationsMap
      );
    },
    [visibleOperationsMap]
  );

  const provideHover = useCallback(
    async (
      model: monaco.editor.ITextModel,
      position: monaco.Position,
      token: monaco.CancellationToken
    ) => {
      const innerText = model.getValue();
      const textRange = model.getFullModelRange();

      const lengthAfterPosition = model.getValueLengthInRange({
        startLineNumber: position.lineNumber,
        startColumn: position.column,
        endLineNumber: textRange.endLineNumber,
        endColumn: textRange.endColumn,
      });
      return getHover(
        model.getValue(),
        innerText.length - lengthAfterPosition,
        visibleOperationsMap
      );
    },
    [visibleOperationsMap]
  );

  const onTypeHandler = useCallback(
    (e: monaco.editor.IModelContentChangedEvent, editor: monaco.editor.IStandaloneCodeEditor) => {
      if (e.isFlush || e.isRedoing || e.isUndoing) {
        return;
      }
      if (e.changes.length === 1) {
        const char = e.changes[0].text;
        if (char !== '=' && char !== "'") {
          return;
        }
        const currentPosition = e.changes[0].range;
        if (currentPosition) {
          const currentText = editor.getValue();
          const offset = monacoPositionToOffset(
            currentText,
            new monaco.Position(currentPosition.startLineNumber, currentPosition.startColumn)
          );
          let tokenInfo = getTokenInfo(currentText, offset + 1);

          if (!tokenInfo && char === "'") {
            // try again this time replacing the current quote with an escaped quote
            const line = currentText;
            const lineEscaped = line.substring(0, offset) + "\\'" + line.substring(offset + 1);
            tokenInfo = getTokenInfo(lineEscaped, offset + 2);
          }

          const isSingleQuoteCase = /'LENS_MATH_MARKER/;
          // Make sure that we are only adding kql='' or lucene='', and also
          // check that the = sign isn't inside the KQL expression like kql='='
          if (tokenInfo) {
            if (
              typeof tokenInfo.ast === 'number' ||
              tokenInfo.ast.type !== 'namedArgument' ||
              !namedArgumentsTypes.has(tokenInfo.ast.name) ||
              (tokenInfo.ast.value !== MARKER && !isSingleQuoteCase.test(tokenInfo.ast.value))
            ) {
              return;
            }
          }

          let editOperation: monaco.editor.IIdentifiedSingleEditOperation | null = null;

          const cursorOffset = 2;
          if (char === '=') {
            // check also the previous char whether it was already a =
            // to avoid infinite loops
            if (!tokenInfo && currentText.charAt(offset - 1) !== '=') {
              editOperation = createEditOperation('=', currentPosition, 1);
            }
            if (tokenInfo) {
              editOperation = createEditOperation(`''`, currentPosition, 1);
            }
          }

          if (!tokenInfo && !editOperation) {
            return;
          }

          if (
            char === "'" &&
            tokenInfo?.ast &&
            typeof tokenInfo.ast !== 'number' &&
            'name' in tokenInfo.ast &&
            tokenInfo.ast.name !== 'shift' &&
            tokenInfo.ast.name !== 'reducedTimeRange'
          ) {
            editOperation = createEditOperation(`\\'`, currentPosition);
          }

          if (editOperation) {
            setTimeout(() => {
              editor.executeEdits(
                'LENS',
                [editOperation!],
                [
                  // After inserting, move the cursor in between the single quotes or after the escaped quote
                  new monaco.Selection(
                    currentPosition.startLineNumber,
                    currentPosition.startColumn + cursorOffset,
                    currentPosition.startLineNumber,
                    currentPosition.startColumn + cursorOffset
                  ),
                ]
              );

              // Need to move these sync to prevent race conditions between a fast user typing a single quote
              // after an = char
              // Timeout is required because otherwise the cursor position is not updated.
              editor.setPosition({
                column: currentPosition.startColumn + cursorOffset,
                lineNumber: currentPosition.startLineNumber,
              });
              if (editOperation?.text !== '=') {
                editor.trigger('lens', 'editor.action.triggerSuggest', {});
              }
            }, 0);
          }
        }
      }
    },
    []
  );

  const codeEditorOptions: CodeEditorProps = {
    languageId: LANGUAGE_ID,
    value: text ?? '',
    onChange: setText,
    options: {
      automaticLayout: true,
      fontSize: 14,
      folding: false,
      lineNumbers: 'off',
      scrollBeyondLastLine: false,
      minimap: { enabled: false },
      wordWrap: isWordWrapped ? 'on' : 'off',
      // Disable suggestions that appear when we don't provide a default suggestion
      wordBasedSuggestions: false,
      autoIndent: 'brackets',
      wrappingIndent: 'none',
      dimension: { width: 320, height: 200 },
      fixedOverflowWidgets: true,
      matchBrackets: 'always',
      // Undocumented Monaco option to force left margin width
      lineDecorationsWidth: 16,
    },
  };

  useEffect(() => {
    // Because the monaco model is owned by Lens, we need to manually attach and remove handlers
    const { dispose: dispose1 } = monaco.languages.registerCompletionItemProvider(LANGUAGE_ID, {
      triggerCharacters: ['.', '(', '=', ' ', ':', `'`],
      provideCompletionItems,
    });
    const { dispose: dispose2 } = monaco.languages.registerSignatureHelpProvider(LANGUAGE_ID, {
      signatureHelpTriggerCharacters: ['(', '='],
      provideSignatureHelp,
    });
    const { dispose: dispose3 } = monaco.languages.registerHoverProvider(LANGUAGE_ID, {
      provideHover,
    });
    return () => {
      dispose1();
      dispose2();
      dispose3();
    };
  }, [provideCompletionItems, provideSignatureHelp, provideHover]);

  // The Monaco editor will lazily load Monaco, which takes a render cycle to trigger. This can cause differences
  // in the behavior of Monaco when it's first loaded and then reloaded.
  return (
    <div
      css={[
        sharedEditorStyles.self(euiThemeContext),
        isFullscreen ? fullscreenEditorStyles : defaultEditorStyles,
      ]}
    >
      {!isFullscreen && (
        <EuiFormLabel
          css={css`
            margin-top: ${euiTheme.size.base};
            margin-bottom: ${euiTheme.size.xs};
          `}
        >
          {i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingFormula', {
            defaultMessage: 'Formula',
          })}
        </EuiFormLabel>
      )}

      <div
        className="lnsFormula"
        css={css({
          backgroundColor: euiTheme.colors.backgroundBaseSubdued,
          border: isFullscreen ? 'none' : euiTheme.border.thin,
          borderRadius: isFullscreen ? 0 : euiTheme.border.radius.medium,
          height: isFullscreen ? '100%' : 'auto',
        })}
      >
        <div
          className="lnsFormula__editor"
          css={css`
            & > * + * {
              border-top: ${euiTheme.border.thin};
            }
          `}
        >
          <div css={sharedEditorStyles.editorHeader(euiThemeContext)}>
            <EuiFlexGroup alignItems="center" gutterSize="m" responsive={false}>
              <EuiFlexItem
                css={css`
                  display: block;
                `}
              >
                <EuiToolTip
                  content={
                    isWordWrapped
                      ? i18n.translate('xpack.lens.formula.disableWordWrapLabel', {
                          defaultMessage: 'Disable word wrap',
                        })
                      : i18n.translate('xpack.lens.formulaEnableWordWrapLabel', {
                          defaultMessage: 'Enable word wrap',
                        })
                  }
                  position="top"
                >
                  <EuiButtonIcon
                    iconType={isWordWrapped ? 'wordWrap' : 'wordWrapDisabled'}
                    display={!isWordWrapped ? 'fill' : undefined}
                    color={'text'}
                    aria-label={
                      isWordWrapped
                        ? i18n.translate('xpack.lens.formula.disableWordWrapLabel', {
                            defaultMessage: 'Disable word wrap',
                          })
                        : i18n.translate('xpack.lens.formulaEnableWordWrapLabel', {
                            defaultMessage: 'Enable word wrap',
                          })
                    }
                    isSelected={!isWordWrapped}
                    onClick={() => {
                      editor1.current?.updateOptions({
                        wordWrap: isWordWrapped ? 'off' : 'on',
                      });
                      toggleWordWrap(!isWordWrapped);
                    }}
                  />
                </EuiToolTip>
              </EuiFlexItem>

              <EuiFlexItem
                css={css`
                  display: block;
                `}
                grow={false}
              >
                <EuiButtonEmpty
                  onClick={() => {
                    toggleFullscreen();
                    // Help text opens when entering full screen, and closes when leaving full screen
                    setIsHelpOpen(!isFullscreen);
                  }}
                  iconType={isFullscreen ? 'fullScreenExit' : 'fullScreen'}
                  size="xs"
                  color="text"
                  flush="right"
                  data-test-subj="lnsFormula-fullscreen"
                >
                  {isFullscreen
                    ? i18n.translate('xpack.lens.formula.fullScreenExitLabel', {
                        defaultMessage: 'Collapse',
                      })
                    : i18n.translate('xpack.lens.formula.fullScreenEnterLabel', {
                        defaultMessage: 'Expand',
                      })}
                </EuiButtonEmpty>
              </EuiFlexItem>
            </EuiFlexGroup>
          </div>

          <div className="lnsFormula__editorContent">
            <CodeEditor
              {...codeEditorOptions}
              transparentBackground={true}
              options={{
                ...codeEditorOptions.options,
                // Shared model and overflow node
                overflowWidgetsDomNode: overflowDiv1.current,
              }}
              editorDidMount={(editor) => {
                editor1.current = editor;
                const model = editor.getModel();
                if (model) {
                  editorModel.current = model;
                }
                // If we ever introduce a second Monaco editor, we need to toggle
                // the typing handler to the active editor to maintain the cursor
                disposables.current.push(
                  editor.onDidChangeModelContent((e) => {
                    onTypeHandler(e, editor);
                  })
                );
              }}
            />

            {!text ? (
              <div css={sharedEditorStyles.editorPlaceholder(euiThemeContext)}>
                <EuiText color="subdued" size="s">
                  {i18n.translate('xpack.lens.formulaPlaceholderText', {
                    defaultMessage: 'Type a formula by combining functions with math, like:',
                  })}
                </EuiText>
                <EuiSpacer size="s" />
                <pre>count() + 1</pre>
              </div>
            ) : null}
          </div>

          <div css={sharedEditorStyles.editorFooter(euiThemeContext)}>
            <EuiFlexGroup alignItems="center" gutterSize="m" responsive={false}>
              <EuiFlexItem grow={false}>
                {isFullscreen ? (
                  <EuiToolTip
                    content={
                      isHelpOpen
                        ? i18n.translate('xpack.lens.formula.editorHelpInlineHideToolTip', {
                            defaultMessage: 'Hide function reference',
                          })
                        : i18n.translate('xpack.lens.formula.editorHelpInlineShowToolTip', {
                            defaultMessage: 'Show function reference',
                          })
                    }
                    delay="long"
                    position="top"
                  >
                    <EuiLink
                      aria-label={i18n.translate('xpack.lens.formula.editorHelpInlineHideLabel', {
                        defaultMessage: 'Hide function reference',
                      })}
                      className="lnsFormula__editorHelp lnsFormula__editorHelp--inline"
                      css={sharedEditorStyles.editorHelpLink(euiThemeContext)}
                      color="text"
                      onClick={() => setIsHelpOpen(!isHelpOpen)}
                    >
                      <EuiIcon type="documentation" />
                      <EuiIcon type={isHelpOpen ? 'arrowDown' : 'arrowUp'} />
                    </EuiLink>
                  </EuiToolTip>
                ) : (
                  <LanguageDocumentationPopover
                    language="Formula"
                    sections={documentationSections}
                    buttonProps={{
                      color: 'text',
                      className: 'lnsFormula__editorHelp lnsFormula__editorHelp--overlay',
                      'data-test-subj': 'ESQLEditor-documentation',
                      'aria-label': i18n.translate(
                        'xpack.lens.formula.editorHelpInlineShowToolTip',
                        {
                          defaultMessage: 'Show function reference',
                        }
                      ),
                    }}
                    isHelpMenuOpen={isHelpOpen}
                    onHelpMenuVisibilityChange={setIsHelpOpen}
                  />
                )}
              </EuiFlexItem>

              {errorCount || warningCount ? (
                <EuiFlexItem grow={false}>
                  <EuiPopover
                    ownFocus={false}
                    isOpen={isWarningOpen}
                    closePopover={() => setIsWarningOpen(false)}
                    button={
                      <EuiButtonEmpty
                        color={errorCount ? 'danger' : 'warning'}
                        css={css`
                          white-space: nowrap;
                        `}
                        className="lnsFormula__editorError"
                        iconType="warning"
                        size="xs"
                        flush="right"
                        onClick={() => {
                          setIsWarningOpen(!isWarningOpen);
                        }}
                      >
                        {errorCount
                          ? i18n.translate('xpack.lens.formulaErrorCount', {
                              defaultMessage: '{count} {count, plural, one {error} other {errors}}',
                              values: { count: errorCount },
                            })
                          : null}
                        {warningCount
                          ? i18n.translate('xpack.lens.formulaWarningCount', {
                              defaultMessage:
                                '{count} {count, plural, one {warning} other {warnings}}',
                              values: { count: warningCount },
                            })
                          : null}
                      </EuiButtonEmpty>
                    }
                  >
                    <div
                      css={css`
                        max-width: ${euiTheme.components.forms.maxWidth};
                      `}
                    >
                      {warnings.map(({ message, severity }, index) => (
                        <div
                          key={index}
                          css={index !== 0 && sharedEditorStyles.warningText(euiThemeContext)}
                        >
                          <EuiText
                            size="s"
                            color={
                              severity === monaco.MarkerSeverity.Warning ? 'warning' : 'danger'
                            }
                          >
                            {message}
                          </EuiText>
                        </div>
                      ))}
                    </div>
                  </EuiPopover>
                </EuiFlexItem>
              ) : null}
            </EuiFlexGroup>
          </div>
        </div>

        {/* fix the css here */}
        {isFullscreen && isHelpOpen ? (
          <div
            className="documentation__docs--inline"
            css={sharedEditorStyles.formulaDocs(euiThemeContext)}
          >
            <LanguageDocumentationPopoverContent
              language="Formula"
              sections={documentationSections}
            />
          </div>
        ) : null}
      </div>
    </div>
  );
}