From d273c07edcc1d0ab00f73309a2fb385b43f6221b Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Wed, 9 Oct 2024 17:54:43 +0200 Subject: [PATCH] [Console] Delete unused sense models and unused files (#195344) --- NOTICE.txt | 31 - src/plugins/console/README.md | 8 +- .../editor_context/editor_registry.ts | 5 +- .../console/public/application/hooks/index.ts | 3 +- .../use_restore_request_from_history/index.ts | 10 - .../restore_request_from_history.ts | 48 - .../restore_request_from_history_to_monaco.ts | 25 - .../use_restore_request_from_history.ts | 21 - .../hooks/use_send_current_request/index.ts | 1 - .../hooks/use_send_current_request/track.ts | 34 - .../use_send_current_request.test.tsx | 130 - .../use_send_current_request.ts | 148 - .../application/hooks/use_set_input_editor.ts | 3 +- .../public/application/models/index.ts | 11 - .../models/legacy_core_editor/create.ts | 20 - .../legacy_core_editor/create_readonly.ts | 81 - .../models/legacy_core_editor/index.ts | 18 - .../models/legacy_core_editor/input.test.js | 559 ---- .../legacy_core_editor.test.mocks.ts | 29 - .../legacy_core_editor/legacy_core_editor.ts | 511 ---- .../models/legacy_core_editor/mode/input.ts | 79 - .../mode/input_highlight_rules.ts | 180 -- .../models/legacy_core_editor/mode/output.ts | 37 - .../mode/output_highlight_rules.test.ts | 56 - .../mode/output_highlight_rules.ts | 64 - .../models/legacy_core_editor/mode/script.ts | 48 - .../legacy_core_editor/mode/worker/index.d.ts | 10 - .../legacy_core_editor/mode/worker/index.js | 15 - .../legacy_core_editor/mode/worker/worker.js | 2392 ----------------- .../output_tokenization.test.js | 91 - .../models/legacy_core_editor/smart_resize.ts | 27 - .../legacy_core_editor/theme_sense_dark.js | 123 - .../__fixtures__/editor_input1.txt | 37 - .../application/models/sense_editor/create.ts | 22 - .../application/models/sense_editor/curl.ts | 194 -- .../application/models/sense_editor/index.ts | 14 - .../models/sense_editor/integration.test.js | 1279 --------- .../models/sense_editor/sense_editor.test.js | 641 ----- .../sense_editor/sense_editor.test.mocks.ts | 20 - .../models/sense_editor/sense_editor.ts | 534 ---- .../public/application/stores/editor.ts | 3 +- .../public/lib/ace_token_provider/index.ts | 10 - .../ace_token_provider/token_provider.test.ts | 223 -- .../lib/ace_token_provider/token_provider.ts | 84 - .../public/lib/autocomplete/autocomplete.ts | 1316 --------- .../get_endpoint_from_position.ts | 33 - .../autocomplete/looks_like_typing_in.test.ts | 224 -- .../lib/autocomplete/looks_like_typing_in.ts | 109 - .../autocomplete_entities.test.js | 1 - .../__fixtures__/curl_parsing.txt | 146 - .../console/public/lib/curl_parsing/curl.js | 194 -- .../lib/curl_parsing/curl_parsing.test.js | 37 - src/plugins/console/public/lib/kb/kb.test.js | 1 - .../console/public/lib/row_parser.test.ts | 107 - src/plugins/console/public/lib/row_parser.ts | 161 -- src/plugins/console/public/styles/_app.scss | 53 - .../console/public/types/core_editor.ts | 5 +- src/plugins/console/tsconfig.json | 2 - .../translations/translations/fr-FR.json | 6 - .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - 61 files changed, 9 insertions(+), 10277 deletions(-) delete mode 100644 src/plugins/console/public/application/hooks/use_restore_request_from_history/index.ts delete mode 100644 src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts delete mode 100644 src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history_to_monaco.ts delete mode 100644 src/plugins/console/public/application/hooks/use_restore_request_from_history/use_restore_request_from_history.ts delete mode 100644 src/plugins/console/public/application/hooks/use_send_current_request/track.ts delete mode 100644 src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx delete mode 100644 src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts delete mode 100644 src/plugins/console/public/application/models/index.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/create.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/create_readonly.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/index.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/input.test.js delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.test.mocks.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/input.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/output.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.test.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/script.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.js delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/worker/worker.js delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/output_tokenization.test.js delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/smart_resize.ts delete mode 100644 src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js delete mode 100644 src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt delete mode 100644 src/plugins/console/public/application/models/sense_editor/create.ts delete mode 100644 src/plugins/console/public/application/models/sense_editor/curl.ts delete mode 100644 src/plugins/console/public/application/models/sense_editor/index.ts delete mode 100644 src/plugins/console/public/application/models/sense_editor/integration.test.js delete mode 100644 src/plugins/console/public/application/models/sense_editor/sense_editor.test.js delete mode 100644 src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts delete mode 100644 src/plugins/console/public/application/models/sense_editor/sense_editor.ts delete mode 100644 src/plugins/console/public/lib/ace_token_provider/index.ts delete mode 100644 src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts delete mode 100644 src/plugins/console/public/lib/ace_token_provider/token_provider.ts delete mode 100644 src/plugins/console/public/lib/autocomplete/autocomplete.ts delete mode 100644 src/plugins/console/public/lib/autocomplete/get_endpoint_from_position.ts delete mode 100644 src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts delete mode 100644 src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts delete mode 100644 src/plugins/console/public/lib/curl_parsing/__fixtures__/curl_parsing.txt delete mode 100644 src/plugins/console/public/lib/curl_parsing/curl.js delete mode 100644 src/plugins/console/public/lib/curl_parsing/curl_parsing.test.js delete mode 100644 src/plugins/console/public/lib/row_parser.test.ts delete mode 100644 src/plugins/console/public/lib/row_parser.ts diff --git a/NOTICE.txt b/NOTICE.txt index 3cee52c089cb4..80d49de19e5db 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -74,37 +74,6 @@ under a "BSD" license. Distributed under the BSD license: -Copyright (c) 2010, Ajax.org B.V. -All rights reserved. - - Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Ajax.org B.V. nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- -This product includes code that is based on Ace editor, which was available -under a "BSD" license. - -Distributed under the BSD license: - Copyright (c) 2010, Ajax.org B.V. All rights reserved. diff --git a/src/plugins/console/README.md b/src/plugins/console/README.md index 02da27229286a..35921de334380 100644 --- a/src/plugins/console/README.md +++ b/src/plugins/console/README.md @@ -44,7 +44,7 @@ POST /_some_endpoint ``` ## Architecture -Console uses Ace editor that is wrapped with [`CoreEditor`](https://github.com/elastic/kibana/blob/main/src/plugins/console/public/types/core_editor.ts), so that if needed it can easily be replaced with another editor, for example Monaco. +Console uses Monaco editor that is wrapped with [`kbn-monaco`](https://github.com/elastic/kibana/blob/main/packages/kbn-monaco/index.ts), so that if needed it can easily be replaced with another editor. The autocomplete logic is located in [`autocomplete`](https://github.com/elastic/kibana/blob/main/src/plugins/console/public/lib/autocomplete) folder. Autocomplete rules are computed by classes in `components` sub-folder. ## Autocomplete definitions @@ -317,8 +317,4 @@ Another change is replacing jQuery with the core http client to communicate with ### Outstanding issues #### Autocomplete suggestions for Kibana API endpoints Console currently supports autocomplete suggestions for Elasticsearch API endpoints. The autocomplete suggestions for Kibana API endpoints are not supported yet. -Related issue: [#130661](https://github.com/elastic/kibana/issues/130661) - -#### Migration to Monaco Editor -Console plugin is currently using Ace Editor and it is planned to migrate to Monaco Editor in the future. -Related issue: [#57435](https://github.com/elastic/kibana/issues/57435) \ No newline at end of file +Related issue: [#130661](https://github.com/elastic/kibana/issues/130661) \ No newline at end of file diff --git a/src/plugins/console/public/application/contexts/editor_context/editor_registry.ts b/src/plugins/console/public/application/contexts/editor_context/editor_registry.ts index 8197ff0460e86..dc7b58ecbd267 100644 --- a/src/plugins/console/public/application/contexts/editor_context/editor_registry.ts +++ b/src/plugins/console/public/application/contexts/editor_context/editor_registry.ts @@ -8,12 +8,11 @@ */ import { MonacoEditorActionsProvider } from '../../containers/editor/monaco_editor_actions_provider'; -import { SenseEditor } from '../../models/sense_editor'; export class EditorRegistry { - private inputEditor: SenseEditor | MonacoEditorActionsProvider | undefined; + private inputEditor: MonacoEditorActionsProvider | undefined; - setInputEditor(inputEditor: SenseEditor | MonacoEditorActionsProvider) { + setInputEditor(inputEditor: MonacoEditorActionsProvider) { this.inputEditor = inputEditor; } diff --git a/src/plugins/console/public/application/hooks/index.ts b/src/plugins/console/public/application/hooks/index.ts index b6b7211a940e4..29c554771dad0 100644 --- a/src/plugins/console/public/application/hooks/index.ts +++ b/src/plugins/console/public/application/hooks/index.ts @@ -8,7 +8,6 @@ */ export { useSetInputEditor } from './use_set_input_editor'; -export { useRestoreRequestFromHistory } from './use_restore_request_from_history'; -export { useSendCurrentRequest, sendRequest } from './use_send_current_request'; +export { sendRequest } from './use_send_current_request'; export { useSaveCurrentTextObject } from './use_save_current_text_object'; export { useDataInit } from './use_data_init'; diff --git a/src/plugins/console/public/application/hooks/use_restore_request_from_history/index.ts b/src/plugins/console/public/application/hooks/use_restore_request_from_history/index.ts deleted file mode 100644 index 47f12868d9bc6..0000000000000 --- a/src/plugins/console/public/application/hooks/use_restore_request_from_history/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export { useRestoreRequestFromHistory } from './use_restore_request_from_history'; diff --git a/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts b/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts deleted file mode 100644 index 897e499dc481e..0000000000000 --- a/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import RowParser from '../../../lib/row_parser'; -import { ESRequest } from '../../../types'; -import { SenseEditor } from '../../models/sense_editor'; -import { formatRequestBodyDoc } from '../../../lib/utils'; - -export function restoreRequestFromHistory(editor: SenseEditor, req: ESRequest) { - const coreEditor = editor.getCoreEditor(); - let pos = coreEditor.getCurrentPosition(); - let prefix = ''; - let suffix = '\n'; - const parser = new RowParser(coreEditor); - if (parser.isStartRequestRow(pos.lineNumber)) { - pos.column = 1; - suffix += '\n'; - } else if (parser.isEndRequestRow(pos.lineNumber)) { - const line = coreEditor.getLineValue(pos.lineNumber); - pos.column = line.length + 1; - prefix = '\n\n'; - } else if (parser.isInBetweenRequestsRow(pos.lineNumber)) { - pos.column = 1; - } else { - pos = editor.nextRequestEnd(pos); - prefix = '\n\n'; - } - - let s = prefix + req.method + ' ' + req.endpoint; - if (req.data) { - const indent = true; - const formattedData = formatRequestBodyDoc([req.data], indent); - s += '\n' + formattedData.data; - } - - s += suffix; - - coreEditor.insert(pos, s); - coreEditor.moveCursorToPosition({ lineNumber: pos.lineNumber + prefix.length, column: 1 }); - coreEditor.clearSelection(); - coreEditor.getContainer().focus(); -} diff --git a/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history_to_monaco.ts b/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history_to_monaco.ts deleted file mode 100644 index 08c2bc6af86a3..0000000000000 --- a/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history_to_monaco.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { formatRequestBodyDoc } from '../../../lib/utils'; -import { MonacoEditorActionsProvider } from '../../containers/editor/monaco_editor_actions_provider'; -import { ESRequest } from '../../../types'; - -export async function restoreRequestFromHistoryToMonaco( - provider: MonacoEditorActionsProvider, - req: ESRequest -) { - let s = req.method + ' ' + req.endpoint; - if (req.data) { - const indent = true; - const formattedData = formatRequestBodyDoc([req.data], indent); - s += '\n' + formattedData.data; - } - await provider.restoreRequestFromHistory(s); -} diff --git a/src/plugins/console/public/application/hooks/use_restore_request_from_history/use_restore_request_from_history.ts b/src/plugins/console/public/application/hooks/use_restore_request_from_history/use_restore_request_from_history.ts deleted file mode 100644 index 5ee0d185923c2..0000000000000 --- a/src/plugins/console/public/application/hooks/use_restore_request_from_history/use_restore_request_from_history.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { useCallback } from 'react'; -import { instance as registry } from '../../contexts/editor_context/editor_registry'; -import { ESRequest } from '../../../types'; -import { restoreRequestFromHistoryToMonaco } from './restore_request_from_history_to_monaco'; -import { MonacoEditorActionsProvider } from '../../containers/editor/monaco_editor_actions_provider'; - -export const useRestoreRequestFromHistory = () => { - return useCallback(async (req: ESRequest) => { - const editor = registry.getInputEditor(); - await restoreRequestFromHistoryToMonaco(editor as MonacoEditorActionsProvider, req); - }, []); -}; diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/index.ts b/src/plugins/console/public/application/hooks/use_send_current_request/index.ts index 753184f67e998..656c0b939cf5b 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request/index.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/index.ts @@ -7,5 +7,4 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export { useSendCurrentRequest } from './use_send_current_request'; export { sendRequest } from './send_request'; diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/track.ts b/src/plugins/console/public/application/hooks/use_send_current_request/track.ts deleted file mode 100644 index e663c0b8354c1..0000000000000 --- a/src/plugins/console/public/application/hooks/use_send_current_request/track.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { SenseEditor } from '../../models/sense_editor'; -import { getEndpointFromPosition } from '../../../lib/autocomplete/get_endpoint_from_position'; -import { MetricsTracker } from '../../../types'; - -export const track = ( - requests: Array<{ method: string }>, - editor: SenseEditor, - trackUiMetric: MetricsTracker -) => { - const coreEditor = editor.getCoreEditor(); - // `getEndpointFromPosition` gets values from the server-side generated JSON files which - // are a combination of JS, automatically generated JSON and manual overrides. That means - // the metrics reported from here will be tied to the definitions in those files. - // See src/legacy/core_plugins/console/server/api_server/spec - const endpointDescription = getEndpointFromPosition( - coreEditor, - coreEditor.getCurrentPosition(), - editor.parser - ); - - if (requests[0] && endpointDescription) { - const eventName = `${requests[0].method}_${endpointDescription.id ?? 'unknown'}`; - trackUiMetric.count(eventName); - } -}; diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx deleted file mode 100644 index 7f3082d5ef3dc..0000000000000 --- a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -jest.mock('./send_request', () => ({ sendRequest: jest.fn() })); -jest.mock('../../contexts/editor_context/editor_registry', () => ({ - instance: { getInputEditor: jest.fn() }, -})); -jest.mock('./track', () => ({ track: jest.fn() })); -jest.mock('../../contexts/request_context', () => ({ useRequestActionContext: jest.fn() })); -jest.mock('../../../lib/utils', () => ({ replaceVariables: jest.fn() })); - -import React from 'react'; -import { renderHook, act } from '@testing-library/react-hooks'; - -import { ContextValue, ServicesContextProvider } from '../../contexts'; -import { serviceContextMock } from '../../contexts/services_context.mock'; -import { useRequestActionContext } from '../../contexts/request_context'; -import { instance as editorRegistry } from '../../contexts/editor_context/editor_registry'; -import * as utils from '../../../lib/utils'; - -import { sendRequest } from './send_request'; -import { useSendCurrentRequest } from './use_send_current_request'; - -describe('useSendCurrentRequest', () => { - let mockContextValue: ContextValue; - let dispatch: (...args: unknown[]) => void; - const contexts = ({ children }: { children: JSX.Element }) => ( - {children} - ); - - beforeEach(() => { - mockContextValue = serviceContextMock.create(); - dispatch = jest.fn(); - (useRequestActionContext as jest.Mock).mockReturnValue(dispatch); - (utils.replaceVariables as jest.Mock).mockReturnValue(['test']); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('calls send request', async () => { - // Set up mocks - (mockContextValue.services.settings.toJSON as jest.Mock).mockReturnValue({}); - // This request should succeed - (sendRequest as jest.Mock).mockResolvedValue([]); - (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ - getRequestsInRange: () => ['test'], - })); - - const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); - await act(() => result.current()); - expect(sendRequest).toHaveBeenCalledWith({ - http: mockContextValue.services.http, - requests: ['test'], - }); - - // Second call should be the request success - const [, [requestSucceededCall]] = (dispatch as jest.Mock).mock.calls; - expect(requestSucceededCall).toEqual({ type: 'requestSuccess', payload: { data: [] } }); - }); - - it('handles known errors', async () => { - // Set up mocks - (sendRequest as jest.Mock).mockRejectedValue({ response: 'nada' }); - (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ - getRequestsInRange: () => ['test'], - })); - - const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); - await act(() => result.current()); - // Second call should be the request failure - const [, [requestFailedCall]] = (dispatch as jest.Mock).mock.calls; - - // The request must have concluded - expect(requestFailedCall).toEqual({ type: 'requestFail', payload: { response: 'nada' } }); - }); - - it('handles unknown errors', async () => { - // Set up mocks - (sendRequest as jest.Mock).mockRejectedValue(NaN /* unexpected error value */); - (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ - getRequestsInRange: () => ['test'], - })); - - const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); - await act(() => result.current()); - // Second call should be the request failure - const [, [requestFailedCall]] = (dispatch as jest.Mock).mock.calls; - - // The request must have concluded - expect(requestFailedCall).toEqual({ type: 'requestFail', payload: undefined }); - // It also notified the user - expect(mockContextValue.services.notifications.toasts.addError).toHaveBeenCalledWith(NaN, { - title: 'Unknown Request Error', - }); - }); - - it('notifies the user about save to history errors once only', async () => { - // Set up mocks - (sendRequest as jest.Mock).mockReturnValue( - [{ request: {} }, { request: {} }] /* two responses to save history */ - ); - (mockContextValue.services.settings.toJSON as jest.Mock).mockReturnValue({ - isHistoryEnabled: true, - }); - (mockContextValue.services.history.addToHistory as jest.Mock).mockImplementation(() => { - // Mock throwing - throw new Error('cannot save!'); - }); - (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ - getRequestsInRange: () => ['test', 'test'], - })); - - const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); - await act(() => result.current()); - - expect(dispatch).toHaveBeenCalledTimes(2); - - expect(mockContextValue.services.history.addToHistory).toHaveBeenCalledTimes(2); - // It only called notification once - expect(mockContextValue.services.notifications.toasts.addError).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts deleted file mode 100644 index afdd5358432e9..0000000000000 --- a/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { i18n } from '@kbn/i18n'; -import { useCallback } from 'react'; - -import { toMountPoint } from '../../../shared_imports'; -import { isQuotaExceededError } from '../../../services/history'; -import { instance as registry } from '../../contexts/editor_context/editor_registry'; -import { useRequestActionContext, useServicesContext } from '../../contexts'; -import { StorageQuotaError } from '../../components/storage_quota_error'; -import { sendRequest } from './send_request'; -import { track } from './track'; -import { replaceVariables } from '../../../lib/utils'; -import { StorageKeys } from '../../../services'; -import { DEFAULT_VARIABLES } from '../../../../common/constants'; -import { SenseEditor } from '../../models'; - -export const useSendCurrentRequest = () => { - const { - services: { history, settings, notifications, trackUiMetric, http, autocompleteInfo, storage }, - ...startServices - } = useServicesContext(); - - const dispatch = useRequestActionContext(); - - return useCallback(async () => { - try { - const editor = registry.getInputEditor() as SenseEditor; - const variables = storage.get(StorageKeys.VARIABLES, DEFAULT_VARIABLES); - let requests = await editor.getRequestsInRange(); - requests = replaceVariables(requests, variables); - if (!requests.length) { - notifications.toasts.add( - i18n.translate('console.notification.error.noRequestSelectedTitle', { - defaultMessage: - 'No request selected. Select a request by placing the cursor inside it.', - }) - ); - return; - } - - dispatch({ type: 'sendRequest', payload: undefined }); - - // Fire and forget - setTimeout(() => track(requests, editor as SenseEditor, trackUiMetric), 0); - - const results = await sendRequest({ http, requests }); - - let saveToHistoryError: undefined | Error; - const { isHistoryEnabled } = settings.toJSON(); - - if (isHistoryEnabled) { - results.forEach(({ request: { path, method, data } }) => { - try { - history.addToHistory(path, method, data); - } catch (e) { - // Grab only the first error - if (!saveToHistoryError) { - saveToHistoryError = e; - } - } - }); - } - - if (saveToHistoryError) { - const errorTitle = i18n.translate('console.notification.error.couldNotSaveRequestTitle', { - defaultMessage: 'Could not save request to Console history.', - }); - if (isQuotaExceededError(saveToHistoryError)) { - const toast = notifications.toasts.addWarning({ - title: i18n.translate('console.notification.error.historyQuotaReachedMessage', { - defaultMessage: - 'Request history is full. Clear the console history or disable saving new requests.', - }), - text: toMountPoint( - StorageQuotaError({ - onClearHistory: () => { - history.clearHistory(); - notifications.toasts.remove(toast); - }, - onDisableSavingToHistory: () => { - settings.setIsHistoryEnabled(false); - notifications.toasts.remove(toast); - }, - }), - startServices - ), - }); - } else { - // Best effort, but still notify the user. - notifications.toasts.addError(saveToHistoryError, { - title: errorTitle, - }); - } - } - - const { polling } = settings.toJSON(); - if (polling) { - // If the user has submitted a request against ES, something in the fields, indices, aliases, - // or templates may have changed, so we'll need to update this data. Assume that if - // the user disables polling they're trying to optimize performance or otherwise - // preserve resources, so they won't want this request sent either. - autocompleteInfo.retrieve(settings, settings.getAutocomplete()); - } - - dispatch({ - type: 'requestSuccess', - payload: { - data: results, - }, - }); - } catch (e) { - if (e?.response) { - dispatch({ - type: 'requestFail', - payload: e, - }); - } else { - dispatch({ - type: 'requestFail', - payload: undefined, - }); - notifications.toasts.addError(e, { - title: i18n.translate('console.notification.error.unknownErrorTitle', { - defaultMessage: 'Unknown Request Error', - }), - }); - } - } - }, [ - storage, - dispatch, - http, - settings, - notifications.toasts, - trackUiMetric, - history, - autocompleteInfo, - startServices, - ]); -}; diff --git a/src/plugins/console/public/application/hooks/use_set_input_editor.ts b/src/plugins/console/public/application/hooks/use_set_input_editor.ts index d6029420a1772..148ede97520ea 100644 --- a/src/plugins/console/public/application/hooks/use_set_input_editor.ts +++ b/src/plugins/console/public/application/hooks/use_set_input_editor.ts @@ -10,14 +10,13 @@ import { useCallback } from 'react'; import { useEditorActionContext } from '../contexts/editor_context'; import { instance as registry } from '../contexts/editor_context/editor_registry'; -import { SenseEditor } from '../models'; import { MonacoEditorActionsProvider } from '../containers/editor/monaco_editor_actions_provider'; export const useSetInputEditor = () => { const dispatch = useEditorActionContext(); return useCallback( - (editor: SenseEditor | MonacoEditorActionsProvider) => { + (editor: MonacoEditorActionsProvider) => { dispatch({ type: 'setInputEditor', payload: editor }); registry.setInputEditor(editor); }, diff --git a/src/plugins/console/public/application/models/index.ts b/src/plugins/console/public/application/models/index.ts deleted file mode 100644 index 0d4a8f474daee..0000000000000 --- a/src/plugins/console/public/application/models/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './legacy_core_editor/legacy_core_editor'; -export * from './sense_editor'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/create.ts b/src/plugins/console/public/application/models/legacy_core_editor/create.ts deleted file mode 100644 index b2631e8d6712b..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/create.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; -import { LegacyCoreEditor } from './legacy_core_editor'; - -export const create = (el: HTMLElement) => { - const actions = document.querySelector('#ConAppEditorActions'); - if (!actions) { - throw new Error('Could not find ConAppEditorActions element!'); - } - const aceEditor = ace.edit(el); - return new LegacyCoreEditor(aceEditor, actions); -}; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/create_readonly.ts b/src/plugins/console/public/application/models/legacy_core_editor/create_readonly.ts deleted file mode 100644 index dc0a95c224395..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/create_readonly.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import _ from 'lodash'; -import ace from 'brace'; -import { Mode } from './mode/output'; -import smartResize from './smart_resize'; - -export interface CustomAceEditor extends ace.Editor { - update: (text: string, mode?: string | Mode, cb?: () => void) => void; - append: (text: string, foldPrevious?: boolean, cb?: () => void) => void; -} - -/** - * Note: using read-only ace editor leaks the Ace editor API - use this as sparingly as possible or - * create an interface for it so that we don't rely directly on vendor APIs. - */ -export function createReadOnlyAceEditor(element: HTMLElement): CustomAceEditor { - const output: CustomAceEditor = ace.acequire('ace/ace').edit(element); - - const outputMode = new Mode(); - - output.$blockScrolling = Infinity; - output.resize = smartResize(output); - output.update = (val, mode, cb) => { - if (typeof mode === 'function') { - cb = mode as () => void; - mode = void 0; - } - - const session = output.getSession(); - const currentMode = val ? mode || outputMode : 'ace/mode/text'; - - // @ts-ignore - // ignore ts error here due to type definition mistake in brace for setMode(mode: string): void; - // this method accepts string or SyntaxMode which is an object. See https://github.com/ajaxorg/ace/blob/13dc911dbc0ea31ca343d5744b3f472767458fc3/ace.d.ts#L467 - session.setMode(currentMode); - session.setValue(val); - if (typeof cb === 'function') { - setTimeout(cb); - } - }; - - output.append = (val: string, foldPrevious?: boolean, cb?: () => void) => { - if (typeof foldPrevious === 'function') { - cb = foldPrevious; - foldPrevious = true; - } - if (_.isUndefined(foldPrevious)) { - foldPrevious = true; - } - const session = output.getSession(); - const lastLine = session.getLength(); - if (foldPrevious) { - output.moveCursorTo(Math.max(0, lastLine - 1), 0); - } - session.insert({ row: lastLine, column: 0 }, '\n' + val); - output.moveCursorTo(lastLine + 1, 0); - if (typeof cb === 'function') { - setTimeout(cb); - } - }; - - (function setupSession(session) { - session.setMode('ace/mode/text'); - (session as unknown as { setFoldStyle: (v: string) => void }).setFoldStyle('markbeginend'); - session.setTabSize(2); - session.setUseWrapMode(true); - })(output.getSession()); - - output.setShowPrintMargin(false); - output.setReadOnly(true); - - return output; -} diff --git a/src/plugins/console/public/application/models/legacy_core_editor/index.ts b/src/plugins/console/public/application/models/legacy_core_editor/index.ts deleted file mode 100644 index e885257520245..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import 'brace'; -import 'brace/ext/language_tools'; -import 'brace/ext/searchbox'; -import 'brace/mode/json'; -import 'brace/mode/text'; - -export * from './legacy_core_editor'; -export * from './create_readonly'; -export * from './create'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/input.test.js b/src/plugins/console/public/application/models/legacy_core_editor/input.test.js deleted file mode 100644 index e472edc1af125..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/input.test.js +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import './legacy_core_editor.test.mocks'; -import RowParser from '../../../lib/row_parser'; -import { createTokenIterator } from '../../factories'; -import $ from 'jquery'; -import { create } from './create'; - -describe('Input', () => { - let coreEditor; - beforeEach(() => { - // Set up our document body - document.body.innerHTML = `
-
-
-
-
`; - - coreEditor = create(document.querySelector('#ConAppEditor')); - - $(coreEditor.getContainer()).show(); - }); - afterEach(() => { - $(coreEditor.getContainer()).hide(); - }); - - describe('.getLineCount', () => { - it('returns the correct line length', async () => { - await coreEditor.setValue('1\n2\n3\n4', true); - expect(coreEditor.getLineCount()).toBe(4); - }); - }); - - describe('Tokenization', () => { - function tokensAsList() { - const iter = createTokenIterator({ - editor: coreEditor, - position: { lineNumber: 1, column: 1 }, - }); - const ret = []; - let t = iter.getCurrentToken(); - const parser = new RowParser(coreEditor); - if (parser.isEmptyToken(t)) { - t = parser.nextNonEmptyToken(iter); - } - while (t) { - ret.push({ value: t.value, type: t.type }); - t = parser.nextNonEmptyToken(iter); - } - - return ret; - } - - let testCount = 0; - - function tokenTest(tokenList, prefix, data) { - if (data && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - if (data) { - if (prefix) { - data = prefix + '\n' + data; - } - } else { - data = prefix; - } - - test('Token test ' + testCount++ + ' prefix: ' + prefix, async function () { - await coreEditor.setValue(data, true); - const tokens = tokensAsList(); - const normTokenList = []; - for (let i = 0; i < tokenList.length; i++) { - normTokenList.push({ type: tokenList[i++], value: tokenList[i] }); - } - - expect(tokens).toEqual(normTokenList); - }); - } - - tokenTest(['method', 'GET', 'url.part', '_search'], 'GET _search'); - - tokenTest(['method', 'GET', 'url.slash', '/', 'url.part', '_search'], 'GET /_search'); - - tokenTest( - [ - 'method', - 'GET', - 'url.protocol_host', - 'http://somehost', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET http://somehost/_search' - ); - - tokenTest(['method', 'GET', 'url.protocol_host', 'http://somehost'], 'GET http://somehost'); - - tokenTest( - ['method', 'GET', 'url.protocol_host', 'http://somehost', 'url.slash', '/'], - 'GET http://somehost/' - ); - - tokenTest( - ['method', 'GET', 'url.protocol_host', 'http://test:user@somehost', 'url.slash', '/'], - 'GET http://test:user@somehost/' - ); - - tokenTest( - ['method', 'GET', 'url.part', '_cluster', 'url.slash', '/', 'url.part', 'nodes'], - 'GET _cluster/nodes' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - '_cluster', - 'url.slash', - '/', - 'url.part', - 'nodes', - ], - 'GET /_cluster/nodes' - ); - - tokenTest( - ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', '_search'], - 'GET index/_search' - ); - - tokenTest(['method', 'GET', 'url.part', 'index'], 'GET index'); - - tokenTest( - ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', 'type'], - 'GET index/type' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - ], - 'GET /index/type/' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET index/type/_search' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - 'url.part', - '_search', - 'url.questionmark', - '?', - 'url.param', - 'value', - 'url.equal', - '=', - 'url.value', - '1', - ], - 'GET index/type/_search?value=1' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index', - 'url.slash', - '/', - 'url.part', - 'type', - 'url.slash', - '/', - 'url.part', - '1', - ], - 'GET index/type/1' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - ], - 'GET /index1,index2/' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET /index1,index2/_search' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - '_search', - ], - 'GET index1,index2/_search' - ); - - tokenTest( - [ - 'method', - 'GET', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - ], - 'GET /index1,index2' - ); - - tokenTest( - ['method', 'GET', 'url.part', 'index1', 'url.comma', ',', 'url.part', 'index2'], - 'GET index1,index2' - ); - - tokenTest( - ['method', 'GET', 'url.slash', '/', 'url.part', 'index1', 'url.comma', ','], - 'GET /index1,' - ); - - tokenTest( - ['method', 'PUT', 'url.slash', '/', 'url.part', 'index', 'url.slash', '/'], - 'PUT /index/' - ); - - tokenTest( - ['method', 'GET', 'url.part', 'index', 'url.slash', '/', 'url.part', '_search'], - 'GET index/_search ' - ); - - tokenTest(['method', 'PUT', 'url.slash', '/', 'url.part', 'index'], 'PUT /index'); - - tokenTest( - [ - 'method', - 'PUT', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - 'type1', - 'url.comma', - ',', - 'url.part', - 'type2', - ], - 'PUT /index1,index2/type1,type2' - ); - - tokenTest( - [ - 'method', - 'PUT', - 'url.slash', - '/', - 'url.part', - 'index1', - 'url.slash', - '/', - 'url.part', - 'type1', - 'url.comma', - ',', - 'url.part', - 'type2', - 'url.comma', - ',', - ], - 'PUT /index1/type1,type2,' - ); - - tokenTest( - [ - 'method', - 'PUT', - 'url.part', - 'index1', - 'url.comma', - ',', - 'url.part', - 'index2', - 'url.slash', - '/', - 'url.part', - 'type1', - 'url.comma', - ',', - 'url.part', - 'type2', - 'url.slash', - '/', - 'url.part', - '1234', - ], - 'PUT index1,index2/type1,type2/1234' - ); - - tokenTest( - [ - 'method', - 'POST', - 'url.part', - '_search', - 'paren.lparen', - '{', - 'variable', - '"q"', - 'punctuation.colon', - ':', - 'paren.lparen', - '{', - 'paren.rparen', - '}', - 'paren.rparen', - '}', - ], - 'POST _search\n' + '{\n' + ' "q": {}\n' + ' \n' + '}' - ); - - tokenTest( - [ - 'method', - 'POST', - 'url.part', - '_search', - 'paren.lparen', - '{', - 'variable', - '"q"', - 'punctuation.colon', - ':', - 'paren.lparen', - '{', - 'variable', - '"s"', - 'punctuation.colon', - ':', - 'paren.lparen', - '{', - 'paren.rparen', - '}', - 'paren.rparen', - '}', - 'paren.rparen', - '}', - ], - 'POST _search\n' + '{\n' + ' "q": { "s": {}}\n' + ' \n' + '}' - ); - - function statesAsList() { - const ret = []; - const maxLine = coreEditor.getLineCount(); - for (let line = 1; line <= maxLine; line++) ret.push(coreEditor.getLineState(line)); - return ret; - } - - function statesTest(statesList, prefix, data) { - if (data && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - if (data) { - if (prefix) { - data = prefix + '\n' + data; - } - } else { - data = prefix; - } - - test('States test ' + testCount++ + ' prefix: ' + prefix, async function () { - await coreEditor.setValue(data, true); - const modes = statesAsList(); - expect(modes).toEqual(statesList); - }); - } - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "query": { "match_all": {} }\n' + '}' - ); - - statesTest( - ['start', 'json', ['json', 'json'], ['json', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "query": { \n' + ' "match_all": {} \n' + ' }\n' + '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": { "source": "" }\n' + '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": ""\n' + '}' - ); - - statesTest( - ['start', 'json', ['json', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": {\n' + ' }\n' + '}' - ); - - statesTest( - [ - 'start', - 'json', - ['script-start', 'json', 'json', 'json'], - ['script-start', 'json', 'json', 'json'], - ['json', 'json'], - 'json', - 'start', - ], - 'POST _search\n' + - '{\n' + - ' "test": { "script": """\n' + - ' test script\n' + - ' """\n' + - ' }\n' + - '}' - ); - - statesTest( - ['start', 'json', ['script-start', 'json'], ['script-start', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": """\n' + ' test script\n' + ' """,\n' + '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "script": """test script""",\n' + '}' - ); - - statesTest( - ['start', 'json', ['string_literal', 'json'], ['string_literal', 'json'], 'json', 'start'], - 'POST _search\n' + '{\n' + ' "something": """\n' + ' test script\n' + ' """,\n' + '}' - ); - - statesTest( - [ - 'start', - 'json', - ['string_literal', 'json', 'json', 'json'], - ['string_literal', 'json', 'json', 'json'], - ['json', 'json'], - ['json', 'json'], - 'json', - 'start', - ], - 'POST _search\n' + - '{\n' + - ' "something": { "f" : """\n' + - ' test script\n' + - ' """,\n' + - ' "g": 1\n' + - ' }\n' + - '}' - ); - - statesTest( - ['start', 'json', 'json', 'start'], - 'POST _search\n' + '{\n' + ' "something": """test script""",\n' + '}' - ); - }); -}); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.test.mocks.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.test.mocks.ts deleted file mode 100644 index 2ef5551e893d1..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.test.mocks.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -jest.mock('./mode/worker', () => { - return { workerModule: { id: 'sense_editor/mode/worker', src: '' } }; -}); - -import '@kbn/web-worker-stub'; - -// @ts-ignore -window.URL = { - createObjectURL: () => { - return ''; - }, -}; - -import 'brace'; -import 'brace/ext/language_tools'; -import 'brace/ext/searchbox'; -import 'brace/mode/json'; -import 'brace/mode/text'; - -document.queryCommandSupported = () => true; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts deleted file mode 100644 index edeb64104be7f..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace, { type Annotation } from 'brace'; -import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace'; -import $ from 'jquery'; -import { - CoreEditor, - Position, - Range, - Token, - TokensProvider, - EditorEvent, - AutoCompleterFunction, -} from '../../../types'; -import { AceTokensProvider } from '../../../lib/ace_token_provider'; -import * as curl from '../sense_editor/curl'; -import smartResize from './smart_resize'; -import * as InputMode from './mode/input'; - -const _AceRange = ace.acequire('ace/range').Range; - -const rangeToAceRange = ({ start, end }: Range) => - new _AceRange(start.lineNumber - 1, start.column - 1, end.lineNumber - 1, end.column - 1); - -export class LegacyCoreEditor implements CoreEditor { - private _aceOnPaste: Function; - $actions: JQuery; - resize: () => void; - - constructor(private readonly editor: IAceEditor, actions: HTMLElement) { - this.$actions = $(actions); - this.editor.setShowPrintMargin(false); - - const session = this.editor.getSession(); - // @ts-expect-error - // ignore ts error here due to type definition mistake in brace for setMode(mode: string): void; - // this method accepts string or SyntaxMode which is an object. See https://github.com/ajaxorg/ace/blob/13dc911dbc0ea31ca343d5744b3f472767458fc3/ace.d.ts#L467 - session.setMode(new InputMode.Mode()); - (session as unknown as { setFoldStyle: (style: string) => void }).setFoldStyle('markbeginend'); - session.setTabSize(2); - session.setUseWrapMode(true); - - this.resize = smartResize(this.editor); - - // Intercept ace on paste handler. - this._aceOnPaste = this.editor.onPaste; - this.editor.onPaste = this.DO_NOT_USE_onPaste.bind(this); - - this.editor.setOptions({ - enableBasicAutocompletion: true, - }); - - this.editor.$blockScrolling = Infinity; - this.hideActionsBar(); - this.editor.focus(); - } - - // dirty check for tokenizer state, uses a lot less cycles - // than listening for tokenizerUpdate - waitForLatestTokens(): Promise { - return new Promise((resolve) => { - const session = this.editor.getSession(); - const checkInterval = 25; - - const check = () => { - // If the bgTokenizer doesn't exist, we can assume that the underlying editor has been - // torn down, e.g. by closing the History tab, and we don't need to do anything further. - if (session.bgTokenizer) { - // Wait until the bgTokenizer is done running before executing the callback. - if ((session.bgTokenizer as unknown as { running: boolean }).running) { - setTimeout(check, checkInterval); - } else { - resolve(); - } - } - }; - - setTimeout(check, 0); - }); - } - - getLineState(lineNumber: number) { - const session = this.editor.getSession(); - return session.getState(lineNumber - 1); - } - - getValueInRange(range: Range): string { - return this.editor.getSession().getTextRange(rangeToAceRange(range)); - } - - getTokenProvider(): TokensProvider { - return new AceTokensProvider(this.editor.getSession()); - } - - getValue(): string { - return this.editor.getValue(); - } - - async setValue(text: string, forceRetokenize: boolean): Promise { - const session = this.editor.getSession(); - session.setValue(text); - if (forceRetokenize) { - await this.forceRetokenize(); - } - } - - getLineValue(lineNumber: number): string { - const session = this.editor.getSession(); - return session.getLine(lineNumber - 1); - } - - getCurrentPosition(): Position { - const cursorPosition = this.editor.getCursorPosition(); - return { - lineNumber: cursorPosition.row + 1, - column: cursorPosition.column + 1, - }; - } - - clearSelection(): void { - this.editor.clearSelection(); - } - - getTokenAt(pos: Position): Token | null { - const provider = this.getTokenProvider(); - return provider.getTokenAt(pos); - } - - insert(valueOrPos: string | Position, value?: string): void { - if (typeof valueOrPos === 'string') { - this.editor.insert(valueOrPos); - return; - } - const document = this.editor.getSession().getDocument(); - document.insert( - { - column: valueOrPos.column - 1, - row: valueOrPos.lineNumber - 1, - }, - value || '' - ); - } - - moveCursorToPosition(pos: Position): void { - this.editor.moveCursorToPosition({ row: pos.lineNumber - 1, column: pos.column - 1 }); - } - - replace(range: Range, value: string): void { - const session = this.editor.getSession(); - session.replace(rangeToAceRange(range), value); - } - - getLines(startLine: number, endLine: number): string[] { - const session = this.editor.getSession(); - return session.getLines(startLine - 1, endLine - 1); - } - - replaceRange(range: Range, value: string) { - const pos = this.editor.getCursorPosition(); - this.editor.getSession().replace(rangeToAceRange(range), value); - - const maxRow = Math.max(range.start.lineNumber - 1 + value.split('\n').length - 1, 1); - pos.row = Math.min(pos.row, maxRow); - this.editor.moveCursorToPosition(pos); - // ACE UPGRADE - check if needed - at the moment the above may trigger a selection. - this.editor.clearSelection(); - } - - getSelectionRange() { - const result = this.editor.getSelectionRange(); - return { - start: { - lineNumber: result.start.row + 1, - column: result.start.column + 1, - }, - end: { - lineNumber: result.end.row + 1, - column: result.end.column + 1, - }, - }; - } - - getLineCount() { - // Only use this function to return line count as it uses - // a cache. - return this.editor.getSession().getLength(); - } - - addMarker(range: Range) { - return this.editor - .getSession() - .addMarker(rangeToAceRange(range), 'ace_snippet-marker', 'fullLine', false); - } - - removeMarker(ref: number) { - this.editor.getSession().removeMarker(ref); - } - - getWrapLimit(): number { - return this.editor.getSession().getWrapLimit(); - } - - on(event: EditorEvent, listener: () => void) { - if (event === 'changeCursor') { - this.editor.getSession().selection.on(event, listener); - } else if (event === 'changeSelection') { - this.editor.on(event, listener); - } else { - this.editor.getSession().on(event, listener); - } - } - - off(event: EditorEvent, listener: () => void) { - if (event === 'changeSelection') { - this.editor.off(event, listener); - } - } - - isCompleterActive() { - return Boolean( - (this.editor as unknown as { completer: { activated: unknown } }).completer && - (this.editor as unknown as { completer: { activated: unknown } }).completer.activated - ); - } - - detachCompleter() { - // In some situations we need to detach the autocomplete suggestions element manually, - // such as when navigating away from Console when the suggestions list is open. - const completer = (this.editor as unknown as { completer: { detach(): void } }).completer; - return completer?.detach(); - } - - private forceRetokenize() { - const session = this.editor.getSession(); - return new Promise((resolve) => { - // force update of tokens, but not on this thread to allow for ace rendering. - setTimeout(function () { - let i; - for (i = 0; i < session.getLength(); i++) { - session.getTokens(i); - } - resolve(); - }); - }); - } - - // eslint-disable-next-line @typescript-eslint/naming-convention - private DO_NOT_USE_onPaste(text: string) { - if (text && curl.detectCURL(text)) { - const curlInput = curl.parseCURL(text); - this.editor.insert(curlInput); - return; - } - this._aceOnPaste.call(this.editor, text); - } - - private setActionsBar = (value: number | null, topOrBottom: 'top' | 'bottom' = 'top') => { - if (value === null) { - this.$actions.css('visibility', 'hidden'); - } else { - if (topOrBottom === 'top') { - this.$actions.css({ - bottom: 'auto', - top: value, - visibility: 'visible', - }); - } else { - this.$actions.css({ - top: 'auto', - bottom: value, - visibility: 'visible', - }); - } - } - }; - - private hideActionsBar = () => { - this.setActionsBar(null); - }; - - execCommand(cmd: string) { - this.editor.execCommand(cmd); - } - - getContainer(): HTMLDivElement { - return this.editor.container as HTMLDivElement; - } - - setStyles(styles: { wrapLines: boolean; fontSize: string }) { - this.editor.getSession().setUseWrapMode(styles.wrapLines); - this.editor.container.style.fontSize = styles.fontSize; - } - - registerKeyboardShortcut(opts: { keys: string; fn: () => void; name: string }): void { - this.editor.commands.addCommand({ - exec: opts.fn, - name: opts.name, - bindKey: opts.keys, - }); - } - - unregisterKeyboardShortcut(command: string) { - // @ts-ignore - this.editor.commands.removeCommand(command); - } - - legacyUpdateUI(range: Range) { - if (!this.$actions) { - return; - } - if (range) { - // elements are positioned relative to the editor's container - // pageY is relative to page, so subtract the offset - // from pageY to get the new top value - const offsetFromPage = $(this.editor.container).offset()!.top; - const startLine = range.start.lineNumber; - const startColumn = range.start.column; - const firstLine = this.getLineValue(startLine); - const maxLineLength = this.getWrapLimit() - 5; - const isWrapping = firstLine.length > maxLineLength; - const totalOffset = offsetFromPage - (window.pageYOffset || 0); - const getScreenCoords = (line: number) => - this.editor.renderer.textToScreenCoordinates(line - 1, startColumn).pageY - totalOffset; - const topOfReq = getScreenCoords(startLine); - - if (topOfReq >= 0) { - const { bottom: maxBottom } = this.editor.container.getBoundingClientRect(); - if (topOfReq > maxBottom - totalOffset) { - this.setActionsBar(0, 'bottom'); - return; - } - let offset = 0; - if (isWrapping) { - // Try get the line height of the text area in pixels. - const textArea = $(this.editor.container.querySelector('textArea')!); - const hasRoomOnNextLine = this.getLineValue(startLine).length < maxLineLength; - if (textArea && hasRoomOnNextLine) { - // Line height + the number of wraps we have on a line. - offset += this.getLineValue(startLine).length * textArea.height()!; - } else { - if (startLine > 1) { - this.setActionsBar(getScreenCoords(startLine - 1)); - return; - } - this.setActionsBar(getScreenCoords(startLine + 1)); - return; - } - } - this.setActionsBar(topOfReq + offset); - return; - } - - const bottomOfReq = - this.editor.renderer.textToScreenCoordinates(range.end.lineNumber, range.end.column).pageY - - offsetFromPage; - - if (bottomOfReq >= 0) { - this.setActionsBar(0); - return; - } - } - } - - registerAutocompleter(autocompleter: AutoCompleterFunction): void { - // Hook into Ace - - // disable standard context based autocompletion. - // @ts-ignore - ace.define( - 'ace/autocomplete/text_completer', - ['require', 'exports', 'module'], - function ( - require: unknown, - exports: { - getCompletions: ( - innerEditor: unknown, - session: unknown, - pos: unknown, - prefix: unknown, - callback: (e: null | Error, values: string[]) => void - ) => void; - } - ) { - exports.getCompletions = function (innerEditor, session, pos, prefix, callback) { - callback(null, []); - }; - } - ); - - const langTools = ace.acequire('ace/ext/language_tools'); - - langTools.setCompleters([ - { - identifierRegexps: [ - /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character - ], - getCompletions: ( - // eslint-disable-next-line @typescript-eslint/naming-convention - DO_NOT_USE_1: IAceEditor, - aceEditSession: IAceEditSession, - pos: { row: number; column: number }, - prefix: string, - callback: (...args: unknown[]) => void - ) => { - const position: Position = { - lineNumber: pos.row + 1, - column: pos.column + 1, - }; - - const getAnnotationControls = () => { - let customAnnotation: Annotation; - return { - setAnnotation(text: string) { - const annotations = aceEditSession.getAnnotations(); - customAnnotation = { - text, - row: pos.row, - column: pos.column, - type: 'warning', - }; - - aceEditSession.setAnnotations([...annotations, customAnnotation]); - }, - removeAnnotation() { - aceEditSession.setAnnotations( - aceEditSession.getAnnotations().filter((a: Annotation) => a !== customAnnotation) - ); - }, - }; - }; - - autocompleter(position, prefix, callback, getAnnotationControls()); - }, - }, - ]); - } - - destroy() { - this.editor.destroy(); - } - - /** - * Formats body of the request in the editor by removing the extra whitespaces at the beginning of lines, - * And adds the correct indentation for each line - * @param reqRange request range to indent - */ - autoIndent(reqRange: Range) { - const session = this.editor.getSession(); - const mode = session.getMode(); - const startRow = reqRange.start.lineNumber; - const endRow = reqRange.end.lineNumber; - const tab = session.getTabString(); - - for (let row = startRow; row <= endRow; row++) { - let prevLineState = ''; - let prevLineIndent = ''; - if (row > 0) { - prevLineState = session.getState(row - 1); - const prevLine = session.getLine(row - 1); - prevLineIndent = mode.getNextLineIndent(prevLineState, prevLine, tab); - } - - const line = session.getLine(row); - // @ts-ignore - // Brace does not expose type definition for mode.$getIndent, though we have access to this method provided by the underlying Ace editor. - // See https://github.com/ajaxorg/ace/blob/87ce087ed1cf20eeabe56fb0894e048d9bc9c481/lib/ace/mode/text.js#L259 - const currLineIndent = mode.$getIndent(line); - if (prevLineIndent !== currLineIndent) { - if (currLineIndent.length > 0) { - // If current line has indentation, remove it. - // Next we will add the correct indentation by looking at the previous line - const range = new _AceRange(row, 0, row, currLineIndent.length); - session.remove(range); - } - if (prevLineIndent.length > 0) { - // If previous line has indentation, add indentation at the current line - session.insert({ row, column: 0 }, prevLineIndent); - } - } - - // Lastly outdent any closing braces - mode.autoOutdent(prevLineState, session, row); - } - } - - getAllFoldRanges(): Range[] { - const session = this.editor.getSession(); - // @ts-ignore - // Brace does not expose type definition for session.getAllFolds, though we have access to this method provided by the underlying Ace editor. - // See https://github.com/ajaxorg/ace/blob/13dc911dbc0ea31ca343d5744b3f472767458fc3/ace.d.ts#L82 - return session.getAllFolds().map((fold) => fold.range); - } - - addFoldsAtRanges(foldRanges: Range[]) { - const session = this.editor.getSession(); - foldRanges.forEach((range) => { - try { - session.addFold('...', _AceRange.fromPoints(range.start, range.end)); - } catch (e) { - // ignore the error if a fold fails - } - }); - } -} diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/input.ts deleted file mode 100644 index 450feec6e9c3d..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; -import { workerModule } from './worker'; -import { ScriptMode } from './script'; - -const TextMode = ace.acequire('ace/mode/text').Mode; - -const MatchingBraceOutdent = ace.acequire('ace/mode/matching_brace_outdent').MatchingBraceOutdent; -const CstyleBehaviour = ace.acequire('ace/mode/behaviour/cstyle').CstyleBehaviour; -const CStyleFoldMode = ace.acequire('ace/mode/folding/cstyle').FoldMode; -const WorkerClient = ace.acequire('ace/worker/worker_client').WorkerClient; -const AceTokenizer = ace.acequire('ace/tokenizer').Tokenizer; - -import { InputHighlightRules } from './input_highlight_rules'; - -export class Mode extends TextMode { - constructor() { - super(); - this.$tokenizer = new AceTokenizer(new InputHighlightRules().getRules()); - this.$outdent = new MatchingBraceOutdent(); - this.$behaviour = new CstyleBehaviour(); - this.foldingRules = new CStyleFoldMode(); - this.createModeDelegates({ - 'script-': ScriptMode, - }); - } -} - -(function (this: Mode) { - this.getCompletions = function () { - // autocomplete is done by the autocomplete module. - return []; - }; - - this.getNextLineIndent = function (state: string, line: string, tab: string) { - let indent = this.$getIndent(line); - - if (state !== 'string_literal') { - const match = line.match(/^.*[\{\(\[]\s*$/); - if (match) { - indent += tab; - } - } - - return indent; - }; - - this.checkOutdent = function (state: unknown, line: string, input: string) { - return this.$outdent.checkOutdent(line, input); - }; - - this.autoOutdent = function (state: unknown, doc: string, row: string) { - this.$outdent.autoOutdent(doc, row); - }; - this.createWorker = function (session: { - getDocument: () => string; - setAnnotations: (arg0: unknown) => void; - }) { - const worker = new WorkerClient(['ace', 'sense_editor'], workerModule, 'SenseWorker'); - worker.attachToDocument(session.getDocument()); - worker.on('error', function (e: { data: unknown }) { - session.setAnnotations([e.data]); - }); - - worker.on('ok', function (anno: { data: unknown }) { - session.setAnnotations(anno.data); - }); - - return worker; - }; -}).call(Mode.prototype); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.ts deleted file mode 100644 index 8a2f64b3c71f4..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.ts +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; -import { addXJsonToRules } from '@kbn/ace'; - -type Token = - | string - | { token?: string; regex?: string; next?: string; push?: boolean; include?: string }; - -export function addEOL( - tokens: Token[], - reg: string | RegExp, - nextIfEOL: string, - normalNext?: string -) { - if (typeof reg === 'object') { - reg = reg.source; - } - return [ - { token: tokens.concat(['whitespace']), regex: reg + '(\\s*)$', next: nextIfEOL }, - { token: tokens, regex: reg, next: normalNext }, - ]; -} - -export const mergeTokens = (...args: any[]) => [].concat.apply([], args); - -const TextHighlightRules = ace.acequire('ace/mode/text_highlight_rules').TextHighlightRules; -// translating this to monaco -export class InputHighlightRules extends TextHighlightRules { - constructor() { - super(); - this.$rules = { - // TODO - 'start-sql': [ - { token: 'whitespace', regex: '\\s+' }, - { token: 'paren.lparen', regex: '{', next: 'json-sql', push: true }, - { regex: '', next: 'start' }, - ], - start: mergeTokens( - [ - // done - { token: 'warning', regex: '#!.*$' }, - // done - { include: 'comments' }, - // done - { token: 'paren.lparen', regex: '{', next: 'json', push: true }, - ], - // done - addEOL(['method'], /([a-zA-Z]+)/, 'start', 'method_sep'), - [ - // done - { - token: 'whitespace', - regex: '\\s+', - }, - // done - { - token: 'text', - regex: '.+?', - }, - ] - ), - method_sep: mergeTokens( - // done - addEOL( - ['whitespace', 'url.protocol_host', 'url.slash'], - /(\s+)(https?:\/\/[^?\/,]+)(\/)/, - 'start', - 'url' - ), - // done - addEOL(['whitespace', 'variable.template'], /(\s+)(\${\w+})/, 'start', 'url'), - // done - addEOL(['whitespace', 'url.protocol_host'], /(\s+)(https?:\/\/[^?\/,]+)/, 'start', 'url'), - // done - addEOL(['whitespace', 'url.slash'], /(\s+)(\/)/, 'start', 'url'), - // done - addEOL(['whitespace'], /(\s+)/, 'start', 'url') - ), - url: mergeTokens( - // done - addEOL(['variable.template'], /(\${\w+})/, 'start'), - // TODO - addEOL(['url.part'], /(_sql)/, 'start-sql', 'url-sql'), - // done - addEOL(['url.part'], /([^?\/,\s]+)/, 'start'), - // done - addEOL(['url.comma'], /(,)/, 'start'), - // done - addEOL(['url.slash'], /(\/)/, 'start'), - // done - addEOL(['url.questionmark'], /(\?)/, 'start', 'urlParams'), - // done - addEOL(['whitespace', 'comment.punctuation', 'comment.line'], /(\s+)(\/\/)(.*$)/, 'start') - ), - urlParams: mergeTokens( - // done - addEOL(['url.param', 'url.equal', 'variable.template'], /([^&=]+)(=)(\${\w+})/, 'start'), - // done - addEOL(['url.param', 'url.equal', 'url.value'], /([^&=]+)(=)([^&]*)/, 'start'), - // done - addEOL(['url.param'], /([^&=]+)/, 'start'), - // done - addEOL(['url.amp'], /(&)/, 'start'), - // done - addEOL(['whitespace', 'comment.punctuation', 'comment.line'], /(\s+)(\/\/)(.*$)/, 'start') - ), - // TODO - 'url-sql': mergeTokens( - addEOL(['url.part'], /([^?\/,\s]+)/, 'start-sql'), - addEOL(['url.comma'], /(,)/, 'start-sql'), - addEOL(['url.slash'], /(\/)/, 'start-sql'), - addEOL(['url.questionmark'], /(\?)/, 'start-sql', 'urlParams-sql') - ), - // TODO - 'urlParams-sql': mergeTokens( - addEOL(['url.param', 'url.equal', 'url.value'], /([^&=]+)(=)([^&]*)/, 'start-sql'), - addEOL(['url.param'], /([^&=]+)/, 'start-sql'), - addEOL(['url.amp'], /(&)/, 'start-sql') - ), - /** - * Each key in this.$rules considered to be a state in state machine. Regular expressions define the tokens for the current state, as well as the transitions into another state. - * See for more details https://cloud9-sdk.readme.io/docs/highlighting-rules#section-defining-states - * * - * Define a state for comments, these comment rules then can be included in other states. E.g. in 'start' and 'json' states by including { include: 'comments' } - * This will avoid duplicating the same rules in other states - */ - comments: [ - { - // Capture a line comment, indicated by # - // done - token: ['comment.punctuation', 'comment.line'], - regex: /(#)(.*$)/, - }, - { - // Begin capturing a block comment, indicated by /* - // done - token: 'comment.punctuation', - regex: /\/\*/, - push: [ - { - // Finish capturing a block comment, indicated by */ - // done - token: 'comment.punctuation', - regex: /\*\//, - next: 'pop', - }, - { - // done - defaultToken: 'comment.block', - }, - ], - }, - { - // Capture a line comment, indicated by // - // done - token: ['comment.punctuation', 'comment.line'], - regex: /(\/\/)(.*$)/, - }, - ], - }; - - addXJsonToRules(this, 'json'); - // Add comment rules to json rule set - this.$rules.json.unshift({ include: 'comments' }); - - this.$rules.json.unshift({ token: 'variable.template', regex: /("\${\w+}")/ }); - - if (this instanceof InputHighlightRules) { - this.normalizeRules(); - } - } -} diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/output.ts deleted file mode 100644 index df7f3c37d55ec..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; - -import { OutputJsonHighlightRules } from './output_highlight_rules'; - -const JSONMode = ace.acequire('ace/mode/json').Mode; -const MatchingBraceOutdent = ace.acequire('ace/mode/matching_brace_outdent').MatchingBraceOutdent; -const CstyleBehaviour = ace.acequire('ace/mode/behaviour/cstyle').CstyleBehaviour; -const CStyleFoldMode = ace.acequire('ace/mode/folding/cstyle').FoldMode; -ace.acequire('ace/worker/worker_client'); -const AceTokenizer = ace.acequire('ace/tokenizer').Tokenizer; - -export class Mode extends JSONMode { - constructor() { - super(); - this.$tokenizer = new AceTokenizer(new OutputJsonHighlightRules().getRules()); - this.$outdent = new MatchingBraceOutdent(); - this.$behaviour = new CstyleBehaviour(); - this.foldingRules = new CStyleFoldMode(); - } -} - -(function (this: Mode) { - this.createWorker = function () { - return null; - }; - - this.$id = 'sense/mode/input'; -}).call(Mode.prototype); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.test.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.test.ts deleted file mode 100644 index a18841aa4dc17..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { mapStatusCodeToBadge } from './output_highlight_rules'; - -describe('mapStatusCodeToBadge', () => { - const testCases = [ - { - description: 'treats 100 as as default', - value: '# PUT test-index 100 Continue', - badge: 'badge.badge--default', - }, - { - description: 'treats 200 as success', - value: '# PUT test-index 200 OK', - badge: 'badge.badge--success', - }, - { - description: 'treats 301 as primary', - value: '# PUT test-index 301 Moved Permanently', - badge: 'badge.badge--primary', - }, - { - description: 'treats 400 as warning', - value: '# PUT test-index 404 Not Found', - badge: 'badge.badge--warning', - }, - { - description: 'treats 502 as danger', - value: '# PUT test-index 502 Bad Gateway', - badge: 'badge.badge--danger', - }, - { - description: 'treats unexpected numbers as danger', - value: '# PUT test-index 666 Demonic Invasion', - badge: 'badge.badge--danger', - }, - { - description: 'treats no numbers as undefined', - value: '# PUT test-index', - badge: undefined, - }, - ]; - - testCases.forEach(({ description, value, badge }) => { - test(description, () => { - expect(mapStatusCodeToBadge(value)).toBe(badge); - }); - }); -}); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.ts deleted file mode 100644 index 765ba3e263f22..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; -import 'brace/mode/json'; -import { addXJsonToRules } from '@kbn/ace'; - -const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules').JsonHighlightRules; - -export const mapStatusCodeToBadge = (value?: string) => { - const regExpMatchArray = value?.match(/\d+/); - if (regExpMatchArray) { - const status = parseInt(regExpMatchArray[0], 10); - if (status <= 199) { - return 'badge.badge--default'; - } - if (status <= 299) { - return 'badge.badge--success'; - } - if (status <= 399) { - return 'badge.badge--primary'; - } - if (status <= 499) { - return 'badge.badge--warning'; - } - return 'badge.badge--danger'; - } -}; - -export class OutputJsonHighlightRules extends JsonHighlightRules { - constructor() { - super(); - this.$rules = {}; - addXJsonToRules(this, 'start'); - this.$rules.start.unshift( - { - token: 'warning', - regex: '#!.*$', - }, - { - token: 'comment', - // match a comment starting with a hash at the start of the line - // ignore status codes and status texts at the end of the line (e.g. # GET _search/foo 200, # GET _search/foo 200 OK) - regex: /#(.*?)(?=[1-5][0-9][0-9]\s(?:[\sA-Za-z]+)|(?:[1-5][0-9][0-9])|$)/, - }, - { - token: mapStatusCodeToBadge, - // match status codes and status texts at the end of the line (e.g. # GET _search/foo 200, # GET _search/foo 200 OK) - // this rule allows us to highlight them with the corresponding badge color (e.g. 200 OK -> badge.badge--success) - regex: /([1-5][0-9][0-9]\s?[\sA-Za-z]+$)/, - } - ); - - if (this instanceof OutputJsonHighlightRules) { - this.normalizeRules(); - } - } -} diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/script.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/script.ts deleted file mode 100644 index f50b6d3abe8ab..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/script.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; -import { ScriptHighlightRules } from '@kbn/ace'; - -const TextMode = ace.acequire('ace/mode/text').Mode; -const MatchingBraceOutdent = ace.acequire('ace/mode/matching_brace_outdent').MatchingBraceOutdent; -const CstyleBehaviour = ace.acequire('ace/mode/behaviour/cstyle').CstyleBehaviour; -const CStyleFoldMode = ace.acequire('ace/mode/folding/cstyle').FoldMode; -ace.acequire('ace/tokenizer'); - -export class ScriptMode extends TextMode { - constructor() { - super(); - this.$outdent = new MatchingBraceOutdent(); - this.$behaviour = new CstyleBehaviour(); - this.foldingRules = new CStyleFoldMode(); - } -} - -(function (this: ScriptMode) { - this.HighlightRules = ScriptHighlightRules; - - this.getNextLineIndent = function (state: unknown, line: string, tab: string) { - let indent = this.$getIndent(line); - const match = line.match(/^.*[\{\[]\s*$/); - if (match) { - indent += tab; - } - - return indent; - }; - - this.checkOutdent = function (state: unknown, line: string, input: string) { - return this.$outdent.checkOutdent(line, input); - }; - - this.autoOutdent = function (state: unknown, doc: string, row: string) { - this.$outdent.autoOutdent(doc, row); - }; -}).call(ScriptMode.prototype); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts deleted file mode 100644 index 8067bec3556ae..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export declare const workerModule: { id: string; src: string }; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.js deleted file mode 100644 index 23f636b79e1a6..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import src from '!!raw-loader!./worker'; - -export const workerModule = { - id: 'sense_editor/mode/worker', - src, -}; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/worker.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/worker.js deleted file mode 100644 index 65567f377cc52..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/worker.js +++ /dev/null @@ -1,2392 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -/* @notice - * - * This product includes code that is based on Ace editor, which was available - * under a "BSD" license. - * - * Distributed under the BSD license: - * - * Copyright (c) 2010, Ajax.org B.V. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Ajax.org B.V. nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* eslint-disable prettier/prettier,prefer-const,eqeqeq,import/no-commonjs,no-undef,no-sequences, - block-scoped-var,no-use-before-define,no-var,one-var,guard-for-in,new-cap,no-nested-ternary,no-redeclare, - no-unused-vars,no-extend-native,no-empty,camelcase,no-proto,@kbn/imports/no_unresolvable_imports */ -/* - This file is loaded up as a blob by Brace to hand to Ace to load as Jsonp - (hence the redefining of everything). It is based on the javascript - mode from the brace distro. -*/ -function init(window) { - function resolveModuleId(id, paths) { - for (let testPath = id, tail = ''; testPath;) { - let alias = paths[testPath]; - if ('string' === typeof alias) return alias + tail; - if (alias) - {return (alias.location.replace(/\/*$/, '/') + (tail || alias.main || alias.name));} - if (alias === !1) return ''; - let i = testPath.lastIndexOf('/'); - if (-1 === i) break; - (tail = testPath.substr(i) + tail), (testPath = testPath.slice(0, i)); - } - return id; - } - if ( - !( - (void 0 !== window.window && window.document) || - (window.acequire && window.define) - ) - ) { - window.console || - ((window.console = function () { - let msgs = Array.prototype.slice.call(arguments, 0); - postMessage({ type: 'log', data: msgs }); - }), - (window.console.error = window.console.warn = window.console.log = window.console.trace = - window.console)), - (window.window = window), - (window.ace = window), - (window.onerror = function (message, file, line, col, err) { - postMessage({ - type: 'error', - data: { - message: message, - data: err.data, - file: file, - line: line, - col: col, - stack: err.stack, - }, - }); - }), - (window.normalizeModule = function (parentId, moduleName) { - if (-1 !== moduleName.indexOf('!')) { - let chunks = moduleName.split('!'); - return ( - window.normalizeModule(parentId, chunks[0]) + - '!' + - window.normalizeModule(parentId, chunks[1]) - ); - } - if ('.' == moduleName.charAt(0)) { - let base = parentId - .split('/') - .slice(0, -1) - .join('/'); - for ( - moduleName = (base ? base + '/' : '') + moduleName; - -1 !== moduleName.indexOf('.') && previous != moduleName; - - ) { - var previous = moduleName; - moduleName = moduleName - .replace(/^\.\//, '') - .replace(/\/\.\//, '/') - .replace(/[^\/]+\/\.\.\//, ''); - } - } - return moduleName; - }), - (window.acequire = function acequire(parentId, id) { - if ((id || ((id = parentId), (parentId = null)), !id.charAt)) - {throw Error( - 'worker.js acequire() accepts only (parentId, id) as arguments' - );} - id = window.normalizeModule(parentId, id); - let module = window.acequire.modules[id]; - if (module) - {return ( - module.initialized || - ((module.initialized = !0), - (module.exports = module.factory().exports)), - module.exports - );} - if (!window.acequire.tlns) return console.log('unable to load ' + id); - let path = resolveModuleId(id, window.acequire.tlns); - return ( - '.js' != path.slice(-3) && (path += '.js'), - (window.acequire.id = id), - (window.acequire.modules[id] = {}), - importScripts(path), - window.acequire(parentId, id) - ); - }), - (window.acequire.modules = {}), - (window.acequire.tlns = {}), - (window.define = function (id, deps, factory) { - if ( - (2 == arguments.length - ? ((factory = deps), - 'string' !== typeof id && ((deps = id), (id = window.acequire.id))) - : 1 == arguments.length && - ((factory = id), (deps = []), (id = window.acequire.id)), - 'function' !== typeof factory) - ) - {return ( - (window.acequire.modules[id] = { - exports: factory, - initialized: !0, - }), - void 0 - );} - deps.length || (deps = ['require', 'exports', 'module']); - let req = function (childId) { - return window.acequire(id, childId); - }; - window.acequire.modules[id] = { - exports: {}, - factory: function () { - let module = this, - returnExports = factory.apply( - this, - deps.map(function (dep) { - switch (dep) { - case 'require': - return req; - case 'exports': - return module.exports; - case 'module': - return module; - default: - return req(dep); - } - }) - ); - return returnExports && (module.exports = returnExports), module; - }, - }; - }), - (window.define.amd = {}), - (acequire.tlns = {}), - (window.initBaseUrls = function (topLevelNamespaces) { - for (let i in topLevelNamespaces) - {acequire.tlns[i] = topLevelNamespaces[i];} - }), - (window.initSender = function () { - let EventEmitter = window.acequire('ace/lib/event_emitter') - .EventEmitter, - oop = window.acequire('ace/lib/oop'), - Sender = function () {}; - return ( - function () { - oop.implement(this, EventEmitter), - (this.callback = function (data, callbackId) { - postMessage({ type: 'call', id: callbackId, data: data }); - }), - (this.emit = function (name, data) { - postMessage({ type: 'event', name: name, data: data }); - }); - }.call(Sender.prototype), - new Sender() - ); - }); - let main = (window.main = null), - sender = (window.sender = null); - window.onmessage = function (e) { - let msg = e.data; - if (msg.event && sender) sender._signal(msg.event, msg.data); - else if (msg.command) - {if (main[msg.command]) main[msg.command].apply(main, msg.args); - else { - if (!window[msg.command]) - throw Error('Unknown command:' + msg.command); - window[msg.command].apply(window, msg.args); - }} - else if (msg.init) { - window.initBaseUrls(msg.tlns), - acequire('ace/lib/es5-shim'), - (sender = window.sender = window.initSender()); - let clazz = acequire(msg.module)[msg.classname]; - main = window.main = new clazz(sender); - } - }; - } -} -init(this); -ace.define('ace/lib/oop', ['require', 'exports', 'module'], function ( - acequire, - exports -) { - (exports.inherits = function (ctor, superCtor) { - (ctor.super_ = superCtor), - (ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: !1, - writable: !0, - configurable: !0, - }, - })); - }), - (exports.mixin = function (obj, mixin) { - for (let key in mixin) obj[key] = mixin[key]; - return obj; - }), - (exports.implement = function (proto, mixin) { - exports.mixin(proto, mixin); - }); -}), -ace.define('ace/range', ['require', 'exports', 'module'], function ( - acequire, - exports -) { - let comparePoints = function (p1, p2) { - return p1.row - p2.row || p1.column - p2.column; - }, - Range = function (startRow, startColumn, endRow, endColumn) { - (this.start = { row: startRow, column: startColumn }), - (this.end = { row: endRow, column: endColumn }); - }; - (function () { - (this.isEqual = function (range) { - return ( - this.start.row === range.start.row && - this.end.row === range.end.row && - this.start.column === range.start.column && - this.end.column === range.end.column - ); - }), - (this.toString = function () { - return ( - 'Range: [' + - this.start.row + - '/' + - this.start.column + - '] -> [' + - this.end.row + - '/' + - this.end.column + - ']' - ); - }), - (this.contains = function (row, column) { - return 0 == this.compare(row, column); - }), - (this.compareRange = function (range) { - let cmp, - end = range.end, - start = range.start; - return ( - (cmp = this.compare(end.row, end.column)), - 1 == cmp - ? ((cmp = this.compare(start.row, start.column)), - 1 == cmp ? 2 : 0 == cmp ? 1 : 0) - : -1 == cmp - ? -2 - : ((cmp = this.compare(start.row, start.column)), - -1 == cmp ? -1 : 1 == cmp ? 42 : 0) - ); - }), - (this.comparePoint = function (p) { - return this.compare(p.row, p.column); - }), - (this.containsRange = function (range) { - return ( - 0 == this.comparePoint(range.start) && - 0 == this.comparePoint(range.end) - ); - }), - (this.intersects = function (range) { - let cmp = this.compareRange(range); - return -1 == cmp || 0 == cmp || 1 == cmp; - }), - (this.isEnd = function (row, column) { - return this.end.row == row && this.end.column == column; - }), - (this.isStart = function (row, column) { - return this.start.row == row && this.start.column == column; - }), - (this.setStart = function (row, column) { - 'object' === typeof row - ? ((this.start.column = row.column), (this.start.row = row.row)) - : ((this.start.row = row), (this.start.column = column)); - }), - (this.setEnd = function (row, column) { - 'object' === typeof row - ? ((this.end.column = row.column), (this.end.row = row.row)) - : ((this.end.row = row), (this.end.column = column)); - }), - (this.inside = function (row, column) { - return 0 == this.compare(row, column) - ? this.isEnd(row, column) || this.isStart(row, column) - ? !1 - : !0 - : !1; - }), - (this.insideStart = function (row, column) { - return 0 == this.compare(row, column) - ? this.isEnd(row, column) - ? !1 - : !0 - : !1; - }), - (this.insideEnd = function (row, column) { - return 0 == this.compare(row, column) - ? this.isStart(row, column) - ? !1 - : !0 - : !1; - }), - (this.compare = function (row, column) { - return this.isMultiLine() || row !== this.start.row - ? this.start.row > row - ? -1 - : row > this.end.row - ? 1 - : this.start.row === row - ? column >= this.start.column - ? 0 - : -1 - : this.end.row === row - ? this.end.column >= column - ? 0 - : 1 - : 0 - : this.start.column > column - ? -1 - : column > this.end.column - ? 1 - : 0; - }), - (this.compareStart = function (row, column) { - return this.start.row == row && this.start.column == column - ? -1 - : this.compare(row, column); - }), - (this.compareEnd = function (row, column) { - return this.end.row == row && this.end.column == column - ? 1 - : this.compare(row, column); - }), - (this.compareInside = function (row, column) { - return this.end.row == row && this.end.column == column - ? 1 - : this.start.row == row && this.start.column == column - ? -1 - : this.compare(row, column); - }), - (this.clipRows = function (firstRow, lastRow) { - if (this.end.row > lastRow) var end = { row: lastRow + 1, column: 0 }; - else if (firstRow > this.end.row) - {var end = { row: firstRow, column: 0 };} - if (this.start.row > lastRow) - {var start = { row: lastRow + 1, column: 0 };} - else if (firstRow > this.start.row) - {var start = { row: firstRow, column: 0 };} - return Range.fromPoints(start || this.start, end || this.end); - }), - (this.extend = function (row, column) { - let cmp = this.compare(row, column); - if (0 == cmp) return this; - if (-1 == cmp) var start = { row: row, column: column }; - else var end = { row: row, column: column }; - return Range.fromPoints(start || this.start, end || this.end); - }), - (this.isEmpty = function () { - return ( - this.start.row === this.end.row && - this.start.column === this.end.column - ); - }), - (this.isMultiLine = function () { - return this.start.row !== this.end.row; - }), - (this.clone = function () { - return Range.fromPoints(this.start, this.end); - }), - (this.collapseRows = function () { - return 0 == this.end.column - ? new Range( - this.start.row, - 0, - Math.max(this.start.row, this.end.row - 1), - 0 - ) - : new Range(this.start.row, 0, this.end.row, 0); - }), - (this.toScreenRange = function (session) { - let screenPosStart = session.documentToScreenPosition(this.start), - screenPosEnd = session.documentToScreenPosition(this.end); - return new Range( - screenPosStart.row, - screenPosStart.column, - screenPosEnd.row, - screenPosEnd.column - ); - }), - (this.moveBy = function (row, column) { - (this.start.row += row), - (this.start.column += column), - (this.end.row += row), - (this.end.column += column); - }); - }.call(Range.prototype), - (Range.fromPoints = function (start, end) { - return new Range(start.row, start.column, end.row, end.column); - }), - (Range.comparePoints = comparePoints), - (Range.comparePoints = function (p1, p2) { - return p1.row - p2.row || p1.column - p2.column; - }), - (exports.Range = Range)); -}), -ace.define('ace/apply_delta', ['require', 'exports', 'module'], function ( - acequire, - exports -) { - exports.applyDelta = function (docLines, delta) { - let row = delta.start.row, - startColumn = delta.start.column, - line = docLines[row] || ''; - switch (delta.action) { - case 'insert': - var lines = delta.lines; - if (1 === lines.length) - {docLines[row] = - line.substring(0, startColumn) + - delta.lines[0] + - line.substring(startColumn);} - else { - let args = [row, 1].concat(delta.lines); - docLines.splice.apply(docLines, args), - (docLines[row] = line.substring(0, startColumn) + docLines[row]), - (docLines[row + delta.lines.length - 1] += line.substring( - startColumn - )); - } - break; - case 'remove': - var endColumn = delta.end.column, - endRow = delta.end.row; - row === endRow - ? (docLines[row] = - line.substring(0, startColumn) + line.substring(endColumn)) - : docLines.splice( - row, - endRow - row + 1, - line.substring(0, startColumn) + - docLines[endRow].substring(endColumn) - ); - } - }; -}), -ace.define( - 'ace/lib/event_emitter', - ['require', 'exports', 'module'], - function (acequire, exports) { - let EventEmitter = {}, - stopPropagation = function () { - this.propagationStopped = !0; - }, - preventDefault = function () { - this.defaultPrevented = !0; - }; - (EventEmitter._emit = EventEmitter._dispatchEvent = function ( - eventName, - e - ) { - this._eventRegistry || (this._eventRegistry = {}), - this._defaultHandlers || (this._defaultHandlers = {}); - let listeners = this._eventRegistry[eventName] || [], - defaultHandler = this._defaultHandlers[eventName]; - if (listeners.length || defaultHandler) { - ('object' === typeof e && e) || (e = {}), - e.type || (e.type = eventName), - e.stopPropagation || (e.stopPropagation = stopPropagation), - e.preventDefault || (e.preventDefault = preventDefault), - (listeners = listeners.slice()); - for ( - let i = 0; - listeners.length > i && - (listeners[i](e, this), !e.propagationStopped); - i++ - ); - return defaultHandler && !e.defaultPrevented - ? defaultHandler(e, this) - : void 0; - } - }), - (EventEmitter._signal = function (eventName, e) { - let listeners = (this._eventRegistry || {})[eventName]; - if (listeners) { - listeners = listeners.slice(); - for (let i = 0; listeners.length > i; i++) listeners[i](e, this); - } - }), - (EventEmitter.once = function (eventName, callback) { - let _self = this; - callback && - this.addEventListener(eventName, function newCallback() { - _self.removeEventListener(eventName, newCallback), - callback.apply(null, arguments); - }); - }), - (EventEmitter.setDefaultHandler = function (eventName, callback) { - let handlers = this._defaultHandlers; - if ( - (handlers || - (handlers = this._defaultHandlers = { _disabled_: {} }), - handlers[eventName]) - ) { - let old = handlers[eventName], - disabled = handlers._disabled_[eventName]; - disabled || (handlers._disabled_[eventName] = disabled = []), - disabled.push(old); - let i = disabled.indexOf(callback); - -1 != i && disabled.splice(i, 1); - } - handlers[eventName] = callback; - }), - (EventEmitter.removeDefaultHandler = function (eventName, callback) { - let handlers = this._defaultHandlers; - if (handlers) { - let disabled = handlers._disabled_[eventName]; - if (handlers[eventName] == callback) - {handlers[eventName], - disabled && this.setDefaultHandler(eventName, disabled.pop());} - else if (disabled) { - let i = disabled.indexOf(callback); - -1 != i && disabled.splice(i, 1); - } - } - }), - (EventEmitter.on = EventEmitter.addEventListener = function ( - eventName, - callback, - capturing - ) { - this._eventRegistry = this._eventRegistry || {}; - let listeners = this._eventRegistry[eventName]; - return ( - listeners || (listeners = this._eventRegistry[eventName] = []), - -1 == listeners.indexOf(callback) && - listeners[capturing ? 'unshift' : 'push'](callback), - callback - ); - }), - (EventEmitter.off = EventEmitter.removeListener = EventEmitter.removeEventListener = function ( - eventName, - callback - ) { - this._eventRegistry = this._eventRegistry || {}; - let listeners = this._eventRegistry[eventName]; - if (listeners) { - let index = listeners.indexOf(callback); - -1 !== index && listeners.splice(index, 1); - } - }), - (EventEmitter.removeAllListeners = function (eventName) { - this._eventRegistry && (this._eventRegistry[eventName] = []); - }), - (exports.EventEmitter = EventEmitter); - } -), -ace.define( - 'ace/anchor', - ['require', 'exports', 'module', 'ace/lib/oop', 'ace/lib/event_emitter'], - function (acequire, exports) { - let oop = acequire('./lib/oop'), - EventEmitter = acequire('./lib/event_emitter').EventEmitter, - Anchor = (exports.Anchor = function (doc, row, column) { - (this.$onChange = this.onChange.bind(this)), - this.attach(doc), - column === void 0 - ? this.setPosition(row.row, row.column) - : this.setPosition(row, column); - }); - (function () { - function $pointsInOrder(point1, point2, equalPointsInOrder) { - let bColIsAfter = equalPointsInOrder - ? point1.column <= point2.column - : point1.column < point2.column; - return ( - point1.row < point2.row || (point1.row == point2.row && bColIsAfter) - ); - } - function $getTransformedPoint(delta, point, moveIfEqual) { - let deltaIsInsert = 'insert' == delta.action, - deltaRowShift = - (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row), - deltaColShift = - (deltaIsInsert ? 1 : -1) * - (delta.end.column - delta.start.column), - deltaStart = delta.start, - deltaEnd = deltaIsInsert ? deltaStart : delta.end; - return $pointsInOrder(point, deltaStart, moveIfEqual) - ? { row: point.row, column: point.column } - : $pointsInOrder(deltaEnd, point, !moveIfEqual) - ? { - row: point.row + deltaRowShift, - column: - point.column + - (point.row == deltaEnd.row ? deltaColShift : 0), - } - : { row: deltaStart.row, column: deltaStart.column }; - } - oop.implement(this, EventEmitter), - (this.getPosition = function () { - return this.$clipPositionToDocument(this.row, this.column); - }), - (this.getDocument = function () { - return this.document; - }), - (this.$insertRight = !1), - (this.onChange = function (delta) { - if ( - !( - (delta.start.row == delta.end.row && - delta.start.row != this.row) || - delta.start.row > this.row - ) - ) { - let point = $getTransformedPoint( - delta, - { row: this.row, column: this.column }, - this.$insertRight - ); - this.setPosition(point.row, point.column, !0); - } - }), - (this.setPosition = function (row, column, noClip) { - let pos; - if ( - ((pos = noClip - ? { row: row, column: column } - : this.$clipPositionToDocument(row, column)), - this.row != pos.row || this.column != pos.column) - ) { - let old = { row: this.row, column: this.column }; - (this.row = pos.row), - (this.column = pos.column), - this._signal('change', { old: old, value: pos }); - } - }), - (this.detach = function () { - this.document.removeEventListener('change', this.$onChange); - }), - (this.attach = function (doc) { - (this.document = doc || this.document), - this.document.on('change', this.$onChange); - }), - (this.$clipPositionToDocument = function (row, column) { - let pos = {}; - return ( - row >= this.document.getLength() - ? ((pos.row = Math.max(0, this.document.getLength() - 1)), - (pos.column = this.document.getLine(pos.row).length)) - : 0 > row - ? ((pos.row = 0), (pos.column = 0)) - : ((pos.row = row), - (pos.column = Math.min( - this.document.getLine(pos.row).length, - Math.max(0, column) - ))), - 0 > column && (pos.column = 0), - pos - ); - }); - }.call(Anchor.prototype)); - } -), -ace.define( - 'ace/document', - [ - 'require', - 'exports', - 'module', - 'ace/lib/oop', - 'ace/apply_delta', - 'ace/lib/event_emitter', - 'ace/range', - 'ace/anchor', - ], - function (acequire, exports) { - let oop = acequire('./lib/oop'), - applyDelta = acequire('./apply_delta').applyDelta, - EventEmitter = acequire('./lib/event_emitter').EventEmitter, - Range = acequire('./range').Range, - Anchor = acequire('./anchor').Anchor, - Document = function (textOrLines) { - (this.$lines = ['']), - 0 === textOrLines.length - ? (this.$lines = ['']) - : Array.isArray(textOrLines) - ? this.insertMergedLines({ row: 0, column: 0 }, textOrLines) - : this.insert({ row: 0, column: 0 }, textOrLines); - }; - (function () { - oop.implement(this, EventEmitter), - (this.setValue = function (text) { - let len = this.getLength() - 1; - this.remove(new Range(0, 0, len, this.getLine(len).length)), - this.insert({ row: 0, column: 0 }, text); - }), - (this.getValue = function () { - return this.getAllLines().join(this.getNewLineCharacter()); - }), - (this.createAnchor = function (row, column) { - return new Anchor(this, row, column); - }), - (this.$split = - 0 === 'aaa'.split(/a/).length - ? function (text) { - return text.replace(/\r\n|\r/g, '\n').split('\n'); - } - : function (text) { - return text.split(/\r\n|\r|\n/); - }), - (this.$detectNewLine = function (text) { - let match = text.match(/^.*?(\r\n|\r|\n)/m); - (this.$autoNewLine = match ? match[1] : '\n'), - this._signal('changeNewLineMode'); - }), - (this.getNewLineCharacter = function () { - switch (this.$newLineMode) { - case 'windows': - return '\r\n'; - case 'unix': - return '\n'; - default: - return this.$autoNewLine || '\n'; - } - }), - (this.$autoNewLine = ''), - (this.$newLineMode = 'auto'), - (this.setNewLineMode = function (newLineMode) { - this.$newLineMode !== newLineMode && - ((this.$newLineMode = newLineMode), - this._signal('changeNewLineMode')); - }), - (this.getNewLineMode = function () { - return this.$newLineMode; - }), - (this.isNewLine = function (text) { - return '\r\n' == text || '\r' == text || '\n' == text; - }), - (this.getLine = function (row) { - return this.$lines[row] || ''; - }), - (this.getLines = function (firstRow, lastRow) { - return this.$lines.slice(firstRow, lastRow + 1); - }), - (this.getAllLines = function () { - return this.getLines(0, this.getLength()); - }), - (this.getLength = function () { - return this.$lines.length; - }), - (this.getTextRange = function (range) { - return this.getLinesForRange(range).join( - this.getNewLineCharacter() - ); - }), - (this.getLinesForRange = function (range) { - let lines; - if (range.start.row === range.end.row) - {lines = [ - this.getLine(range.start.row).substring( - range.start.column, - range.end.column - ), - ];} - else { - (lines = this.getLines(range.start.row, range.end.row)), - (lines[0] = (lines[0] || '').substring(range.start.column)); - let l = lines.length - 1; - range.end.row - range.start.row == l && - (lines[l] = lines[l].substring(0, range.end.column)); - } - return lines; - }), - (this.insertLines = function (row, lines) { - return ( - console.warn( - 'Use of document.insertLines is deprecated. Use the insertFullLines method instead.' - ), - this.insertFullLines(row, lines) - ); - }), - (this.removeLines = function (firstRow, lastRow) { - return ( - console.warn( - 'Use of document.removeLines is deprecated. Use the removeFullLines method instead.' - ), - this.removeFullLines(firstRow, lastRow) - ); - }), - (this.insertNewLine = function (position) { - return ( - console.warn( - 'Use of document.insertNewLine is deprecated. Use insertMergedLines(position, [\'\', \'\']) instead.' - ), - this.insertMergedLines(position, ['', '']) - ); - }), - (this.insert = function (position, text) { - return ( - 1 >= this.getLength() && this.$detectNewLine(text), - this.insertMergedLines(position, this.$split(text)) - ); - }), - (this.insertInLine = function (position, text) { - let start = this.clippedPos(position.row, position.column), - end = this.pos(position.row, position.column + text.length); - return ( - this.applyDelta( - { start: start, end: end, action: 'insert', lines: [text] }, - !0 - ), - this.clonePos(end) - ); - }), - (this.clippedPos = function (row, column) { - let length = this.getLength(); - void 0 === row - ? (row = length) - : 0 > row - ? (row = 0) - : row >= length && ((row = length - 1), (column = void 0)); - let line = this.getLine(row); - return ( - void 0 == column && (column = line.length), - (column = Math.min(Math.max(column, 0), line.length)), - { row: row, column: column } - ); - }), - (this.clonePos = function (pos) { - return { row: pos.row, column: pos.column }; - }), - (this.pos = function (row, column) { - return { row: row, column: column }; - }), - (this.$clipPosition = function (position) { - let length = this.getLength(); - return ( - position.row >= length - ? ((position.row = Math.max(0, length - 1)), - (position.column = this.getLine(length - 1).length)) - : ((position.row = Math.max(0, position.row)), - (position.column = Math.min( - Math.max(position.column, 0), - this.getLine(position.row).length - ))), - position - ); - }), - (this.insertFullLines = function (row, lines) { - row = Math.min(Math.max(row, 0), this.getLength()); - let column = 0; - this.getLength() > row - ? ((lines = lines.concat([''])), (column = 0)) - : ((lines = [''].concat(lines)), - row--, - (column = this.$lines[row].length)), - this.insertMergedLines({ row: row, column: column }, lines); - }), - (this.insertMergedLines = function (position, lines) { - let start = this.clippedPos(position.row, position.column), - end = { - row: start.row + lines.length - 1, - column: - (1 == lines.length ? start.column : 0) + - lines[lines.length - 1].length, - }; - return ( - this.applyDelta({ - start: start, - end: end, - action: 'insert', - lines: lines, - }), - this.clonePos(end) - ); - }), - (this.remove = function (range) { - let start = this.clippedPos(range.start.row, range.start.column), - end = this.clippedPos(range.end.row, range.end.column); - return ( - this.applyDelta({ - start: start, - end: end, - action: 'remove', - lines: this.getLinesForRange({ start: start, end: end }), - }), - this.clonePos(start) - ); - }), - (this.removeInLine = function (row, startColumn, endColumn) { - let start = this.clippedPos(row, startColumn), - end = this.clippedPos(row, endColumn); - return ( - this.applyDelta( - { - start: start, - end: end, - action: 'remove', - lines: this.getLinesForRange({ start: start, end: end }), - }, - !0 - ), - this.clonePos(start) - ); - }), - (this.removeFullLines = function (firstRow, lastRow) { - (firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1)), - (lastRow = Math.min(Math.max(0, lastRow), this.getLength() - 1)); - let deleteFirstNewLine = - lastRow == this.getLength() - 1 && firstRow > 0, - deleteLastNewLine = this.getLength() - 1 > lastRow, - startRow = deleteFirstNewLine ? firstRow - 1 : firstRow, - startCol = deleteFirstNewLine ? this.getLine(startRow).length : 0, - endRow = deleteLastNewLine ? lastRow + 1 : lastRow, - endCol = deleteLastNewLine ? 0 : this.getLine(endRow).length, - range = new Range(startRow, startCol, endRow, endCol), - deletedLines = this.$lines.slice(firstRow, lastRow + 1); - return ( - this.applyDelta({ - start: range.start, - end: range.end, - action: 'remove', - lines: this.getLinesForRange(range), - }), - deletedLines - ); - }), - (this.removeNewLine = function (row) { - this.getLength() - 1 > row && - row >= 0 && - this.applyDelta({ - start: this.pos(row, this.getLine(row).length), - end: this.pos(row + 1, 0), - action: 'remove', - lines: ['', ''], - }); - }), - (this.replace = function (range, text) { - if ( - (range instanceof Range || - (range = Range.fromPoints(range.start, range.end)), - 0 === text.length && range.isEmpty()) - ) - {return range.start;} - if (text == this.getTextRange(range)) return range.end; - this.remove(range); - let end; - return (end = text ? this.insert(range.start, text) : range.start); - }), - (this.applyDeltas = function (deltas) { - for (let i = 0; deltas.length > i; i++) this.applyDelta(deltas[i]); - }), - (this.revertDeltas = function (deltas) { - for (let i = deltas.length - 1; i >= 0; i--) - {this.revertDelta(deltas[i]);} - }), - (this.applyDelta = function (delta, doNotValidate) { - let isInsert = 'insert' == delta.action; - (isInsert - ? 1 >= delta.lines.length && !delta.lines[0] - : !Range.comparePoints(delta.start, delta.end)) || - (isInsert && - delta.lines.length > 2e4 && - this.$splitAndapplyLargeDelta(delta, 2e4), - applyDelta(this.$lines, delta, doNotValidate), - this._signal('change', delta)); - }), - (this.$splitAndapplyLargeDelta = function (delta, MAX) { - for ( - let lines = delta.lines, - l = lines.length, - row = delta.start.row, - column = delta.start.column, - from = 0, - to = 0; - ; - - ) { - (from = to), (to += MAX - 1); - let chunk = lines.slice(from, to); - if (to > l) { - (delta.lines = chunk), - (delta.start.row = row + from), - (delta.start.column = column); - break; - } - chunk.push(''), - this.applyDelta( - { - start: this.pos(row + from, column), - end: this.pos(row + to, (column = 0)), - action: delta.action, - lines: chunk, - }, - !0 - ); - } - }), - (this.revertDelta = function (delta) { - this.applyDelta({ - start: this.clonePos(delta.start), - end: this.clonePos(delta.end), - action: 'insert' == delta.action ? 'remove' : 'insert', - lines: delta.lines.slice(), - }); - }), - (this.indexToPosition = function (index, startRow) { - for ( - var lines = this.$lines || this.getAllLines(), - newlineLength = this.getNewLineCharacter().length, - i = startRow || 0, - l = lines.length; - l > i; - i++ - ) - {if (((index -= lines[i].length + newlineLength), 0 > index)) - return { - row: i, - column: index + lines[i].length + newlineLength, - };} - return { row: l - 1, column: lines[l - 1].length }; - }), - (this.positionToIndex = function (pos, startRow) { - for ( - var lines = this.$lines || this.getAllLines(), - newlineLength = this.getNewLineCharacter().length, - index = 0, - row = Math.min(pos.row, lines.length), - i = startRow || 0; - row > i; - ++i - ) - {index += lines[i].length + newlineLength;} - return index + pos.column; - }); - }.call(Document.prototype), - (exports.Document = Document)); - } -), -ace.define('ace/lib/lang', ['require', 'exports', 'module'], function ( - acequire, - exports -) { - (exports.last = function (a) { - return a[a.length - 1]; - }), - (exports.stringReverse = function (string) { - return string - .split('') - .reverse() - .join(''); - }), - (exports.stringRepeat = function (string, count) { - for (var result = ''; count > 0;) - {1 & count && (result += string), (count >>= 1) && (string += string);} - return result; - }); - let trimBeginRegexp = /^\s\s*/, - trimEndRegexp = /\s\s*$/; - (exports.stringTrimLeft = function (string) { - return string.replace(trimBeginRegexp, ''); - }), - (exports.stringTrimRight = function (string) { - return string.replace(trimEndRegexp, ''); - }), - (exports.copyObject = function (obj) { - let copy = {}; - for (let key in obj) copy[key] = obj[key]; - return copy; - }), - (exports.copyArray = function (array) { - for (var copy = [], i = 0, l = array.length; l > i; i++) - {copy[i] = - array[i] && 'object' == typeof array[i] - ? this.copyObject(array[i]) - : array[i];} - return copy; - }), - (exports.deepCopy = function deepCopy(obj) { - if ('object' !== typeof obj || !obj) return obj; - let copy; - if (Array.isArray(obj)) { - copy = []; - for (var key = 0; obj.length > key; key++) - {copy[key] = deepCopy(obj[key]);} - return copy; - } - if ('[object Object]' !== Object.prototype.toString.call(obj)) - {return obj;} - copy = {}; - for (var key in obj) copy[key] = deepCopy(obj[key]); - return copy; - }), - (exports.arrayToMap = function (arr) { - for (var map = {}, i = 0; arr.length > i; i++) map[arr[i]] = 1; - return map; - }), - (exports.createMap = function (props) { - let map = Object.create(null); - for (let i in props) map[i] = props[i]; - return map; - }), - (exports.arrayRemove = function (array, value) { - for (let i = 0; array.length >= i; i++) - {value === array[i] && array.splice(i, 1);} - }), - (exports.escapeRegExp = function (str) { - return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); - }), - (exports.escapeHTML = function (str) { - return str - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(/ i; i += 2) { - if (Array.isArray(data[i + 1])) - var d = { - action: 'insert', - start: data[i], - lines: data[i + 1], - }; - else - var d = { - action: 'remove', - start: data[i], - end: data[i + 1], - }; - doc.applyDelta(d, !0); - }} - return _self.$timeout - ? deferredUpdate.schedule(_self.$timeout) - : (_self.onUpdate(), void 0); - }); - }); - (function () { - (this.$timeout = 500), - (this.setTimeout = function (timeout) { - this.$timeout = timeout; - }), - (this.setValue = function (value) { - this.doc.setValue(value), - this.deferredUpdate.schedule(this.$timeout); - }), - (this.getValue = function (callbackId) { - this.sender.callback(this.doc.getValue(), callbackId); - }), - (this.onUpdate = function () {}), - (this.isPending = function () { - return this.deferredUpdate.isPending(); - }); - }.call(Mirror.prototype)); - } -), -ace.define('ace/lib/es5-shim', ['require', 'exports', 'module'], function () { - function Empty() {} - function doesDefinePropertyWork(object) { - try { - return ( - Object.defineProperty(object, 'sentinel', {}), 'sentinel' in object - ); - } catch (exception) {} - } - function toInteger(n) { - return ( - (n = +n), - n !== n - ? (n = 0) - : 0 !== n && - n !== 1 / 0 && - n !== -(1 / 0) && - (n = (n > 0 || -1) * Math.floor(Math.abs(n))), - n - ); - } - Function.prototype.bind || - (Function.prototype.bind = function (that) { - let target = this; - if ('function' !== typeof target) - {throw new TypeError( - 'Function.prototype.bind called on incompatible ' + target - );} - var args = slice.call(arguments, 1), - bound = function () { - if (this instanceof bound) { - let result = target.apply( - this, - args.concat(slice.call(arguments)) - ); - return Object(result) === result ? result : this; - } - return target.apply(that, args.concat(slice.call(arguments))); - }; - return ( - target.prototype && - ((Empty.prototype = target.prototype), - (bound.prototype = new Empty()), - (Empty.prototype = null)), - bound - ); - }); - var defineGetter, - defineSetter, - lookupGetter, - lookupSetter, - supportsAccessors, - call = Function.prototype.call, - prototypeOfArray = Array.prototype, - prototypeOfObject = Object.prototype, - slice = prototypeOfArray.slice, - _toString = call.bind(prototypeOfObject.toString), - owns = call.bind(prototypeOfObject.hasOwnProperty); - if ( - ((supportsAccessors = owns(prototypeOfObject, '__defineGetter__')) && - ((defineGetter = call.bind(prototypeOfObject.__defineGetter__)), - (defineSetter = call.bind(prototypeOfObject.__defineSetter__)), - (lookupGetter = call.bind(prototypeOfObject.__lookupGetter__)), - (lookupSetter = call.bind(prototypeOfObject.__lookupSetter__))), - 2 != [1, 2].splice(0).length) - ) - {if ( - (function() { - function makeArray(l) { - var a = Array(l + 2); - return (a[0] = a[1] = 0), a; - } - var lengthBefore, - array = []; - return ( - array.splice.apply(array, makeArray(20)), - array.splice.apply(array, makeArray(26)), - (lengthBefore = array.length), - array.splice(5, 0, 'XXX'), - lengthBefore + 1 == array.length, - lengthBefore + 1 == array.length ? !0 : void 0 - ); - })() - ) { - var array_splice = Array.prototype.splice; - Array.prototype.splice = function(start, deleteCount) { - return arguments.length - ? array_splice.apply( - this, - [ - void 0 === start ? 0 : start, - void 0 === deleteCount ? this.length - start : deleteCount, - ].concat(slice.call(arguments, 2)) - ) - : []; - }; - } else - Array.prototype.splice = function(pos, removeCount) { - var length = this.length; - pos > 0 - ? pos > length && (pos = length) - : void 0 == pos - ? (pos = 0) - : 0 > pos && (pos = Math.max(length + pos, 0)), - length > pos + removeCount || (removeCount = length - pos); - var removed = this.slice(pos, pos + removeCount), - insert = slice.call(arguments, 2), - add = insert.length; - if (pos === length) add && this.push.apply(this, insert); - else { - var remove = Math.min(removeCount, length - pos), - tailOldPos = pos + remove, - tailNewPos = tailOldPos + add - remove, - tailCount = length - tailOldPos, - lengthAfterRemove = length - remove; - if (tailOldPos > tailNewPos) - for (var i = 0; tailCount > i; ++i) - this[tailNewPos + i] = this[tailOldPos + i]; - else if (tailNewPos > tailOldPos) - for (i = tailCount; i--; ) - this[tailNewPos + i] = this[tailOldPos + i]; - if (add && pos === lengthAfterRemove) - (this.length = lengthAfterRemove), this.push.apply(this, insert); - else - for (this.length = lengthAfterRemove + add, i = 0; add > i; ++i) - this[pos + i] = insert[i]; - } - return removed; - };} - Array.isArray || - (Array.isArray = function (obj) { - return '[object Array]' == _toString(obj); - }); - let boxedString = Object('a'), - splitString = 'a' != boxedString[0] || !(0 in boxedString); - if ( - (Array.prototype.forEach || - (Array.prototype.forEach = function (fun) { - let object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - thisp = arguments[1], - i = -1, - length = self.length >>> 0; - if ('[object Function]' != _toString(fun)) throw new TypeError(); - for (; length > ++i;) - {i in self && fun.call(thisp, self[i], i, object);} - }), - Array.prototype.map || - (Array.prototype.map = function (fun) { - let object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - length = self.length >>> 0, - result = Array(length), - thisp = arguments[1]; - if ('[object Function]' != _toString(fun)) - {throw new TypeError(fun + ' is not a function');} - for (let i = 0; length > i; i++) - {i in self && (result[i] = fun.call(thisp, self[i], i, object));} - return result; - }), - Array.prototype.filter || - (Array.prototype.filter = function (fun) { - let value, - object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - length = self.length >>> 0, - result = [], - thisp = arguments[1]; - if ('[object Function]' != _toString(fun)) - {throw new TypeError(fun + ' is not a function');} - for (let i = 0; length > i; i++) - {i in self && - ((value = self[i]), - fun.call(thisp, value, i, object) && result.push(value));} - return result; - }), - Array.prototype.every || - (Array.prototype.every = function (fun) { - let object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - length = self.length >>> 0, - thisp = arguments[1]; - if ('[object Function]' != _toString(fun)) - {throw new TypeError(fun + ' is not a function');} - for (let i = 0; length > i; i++) - {if (i in self && !fun.call(thisp, self[i], i, object)) return !1;} - return !0; - }), - Array.prototype.some || - (Array.prototype.some = function (fun) { - let object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - length = self.length >>> 0, - thisp = arguments[1]; - if ('[object Function]' != _toString(fun)) - {throw new TypeError(fun + ' is not a function');} - for (let i = 0; length > i; i++) - {if (i in self && fun.call(thisp, self[i], i, object)) return !0;} - return !1; - }), - Array.prototype.reduce || - (Array.prototype.reduce = function (fun) { - let object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - length = self.length >>> 0; - if ('[object Function]' != _toString(fun)) - {throw new TypeError(fun + ' is not a function');} - if (!length && 1 == arguments.length) - {throw new TypeError('reduce of empty array with no initial value');} - let result, - i = 0; - if (arguments.length >= 2) result = arguments[1]; - else - {for (;;) { - if (i in self) { - result = self[i++]; - break; - } - if (++i >= length) - throw new TypeError( - 'reduce of empty array with no initial value' - ); - }} - for (; length > i; i++) - {i in self && - (result = fun.call(void 0, result, self[i], i, object));} - return result; - }), - Array.prototype.reduceRight || - (Array.prototype.reduceRight = function (fun) { - let object = toObject(this), - self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : object, - length = self.length >>> 0; - if ('[object Function]' != _toString(fun)) - {throw new TypeError(fun + ' is not a function');} - if (!length && 1 == arguments.length) - {throw new TypeError( - 'reduceRight of empty array with no initial value' - );} - let result, - i = length - 1; - if (arguments.length >= 2) result = arguments[1]; - else - {for (;;) { - if (i in self) { - result = self[i--]; - break; - } - if (0 > --i) - throw new TypeError( - 'reduceRight of empty array with no initial value' - ); - }} - do - {i in this && - (result = fun.call(void 0, result, self[i], i, object));} - while (i--); - return result; - }), - (Array.prototype.indexOf && -1 == [0, 1].indexOf(1, 2)) || - (Array.prototype.indexOf = function (sought) { - let self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : toObject(this), - length = self.length >>> 0; - if (!length) return -1; - let i = 0; - for ( - arguments.length > 1 && (i = toInteger(arguments[1])), - i = i >= 0 ? i : Math.max(0, length + i); - length > i; - i++ - ) - {if (i in self && self[i] === sought) return i;} - return -1; - }), - (Array.prototype.lastIndexOf && -1 == [0, 1].lastIndexOf(0, -3)) || - (Array.prototype.lastIndexOf = function (sought) { - let self = - splitString && '[object String]' == _toString(this) - ? this.split('') - : toObject(this), - length = self.length >>> 0; - if (!length) return -1; - let i = length - 1; - for ( - arguments.length > 1 && (i = Math.min(i, toInteger(arguments[1]))), - i = i >= 0 ? i : length - Math.abs(i); - i >= 0; - i-- - ) - {if (i in self && sought === self[i]) return i;} - return -1; - }), - Object.getPrototypeOf || - (Object.getPrototypeOf = function (object) { - return ( - object.__proto__ || - (object.constructor - ? object.constructor.prototype - : prototypeOfObject) - ); - }), - !Object.getOwnPropertyDescriptor) - ) { - let ERR_NON_OBJECT = - 'Object.getOwnPropertyDescriptor called on a non-object: '; - Object.getOwnPropertyDescriptor = function (object, property) { - if ( - ('object' !== typeof object && 'function' !== typeof object) || - null === object - ) - {throw new TypeError(ERR_NON_OBJECT + object);} - if (owns(object, property)) { - var descriptor, getter, setter; - if ( - ((descriptor = { enumerable: !0, configurable: !0 }), - supportsAccessors) - ) { - let prototype = object.__proto__; - object.__proto__ = prototypeOfObject; - var getter = lookupGetter(object, property), - setter = lookupSetter(object, property); - if (((object.__proto__ = prototype), getter || setter)) - {return ( - getter && (descriptor.get = getter), - setter && (descriptor.set = setter), - descriptor - );} - } - return (descriptor.value = object[property]), descriptor; - } - }; - } - if ( - (Object.getOwnPropertyNames || - (Object.getOwnPropertyNames = function (object) { - return Object.keys(object); - }), - !Object.create) - ) { - let createEmpty; - (createEmpty = - null === Object.prototype.__proto__ - ? function () { - return { __proto__: null }; - } - : function () { - let empty = {}; - for (let i in empty) empty[i] = null; - return ( - (empty.constructor = empty.hasOwnProperty = empty.propertyIsEnumerable = empty.isPrototypeOf = empty.toLocaleString = empty.toString = empty.valueOf = empty.__proto__ = null), - empty - ); - }), - (Object.create = function (prototype, properties) { - let object; - if (null === prototype) object = createEmpty(); - else { - if ('object' !== typeof prototype) - {throw new TypeError( - 'typeof prototype[' + typeof prototype + "] != 'object'" - );} - let Type = function () {}; - (Type.prototype = prototype), - (object = new Type()), - (object.__proto__ = prototype); - } - return ( - void 0 !== properties && - Object.defineProperties(object, properties), - object - ); - }); - } - if (Object.defineProperty) { - let definePropertyWorksOnObject = doesDefinePropertyWork({}), - definePropertyWorksOnDom = - 'undefined' === typeof document || - doesDefinePropertyWork(document.createElement('div')); - if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) - {var definePropertyFallback = Object.defineProperty;} - } - if (!Object.defineProperty || definePropertyFallback) { - let ERR_NON_OBJECT_DESCRIPTOR = - 'Property description must be an object: ', - ERR_NON_OBJECT_TARGET = 'Object.defineProperty called on non-object: ', - ERR_ACCESSORS_NOT_SUPPORTED = - 'getters & setters can not be defined on this javascript engine'; - Object.defineProperty = function (object, property, descriptor) { - if ( - ('object' !== typeof object && 'function' !== typeof object) || - null === object - ) - {throw new TypeError(ERR_NON_OBJECT_TARGET + object);} - if ( - ('object' !== typeof descriptor && 'function' !== typeof descriptor) || - null === descriptor - ) - {throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);} - if (definePropertyFallback) - {try { - return definePropertyFallback.call( - Object, - object, - property, - descriptor - ); - } catch (exception) {}} - if (owns(descriptor, 'value')) - {if ( - supportsAccessors && - (lookupGetter(object, property) || lookupSetter(object, property)) - ) { - var prototype = object.__proto__; - (object.__proto__ = prototypeOfObject), - delete object[property], - (object[property] = descriptor.value), - (object.__proto__ = prototype); - } else object[property] = descriptor.value;} - else { - if (!supportsAccessors) - {throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);} - owns(descriptor, 'get') && - defineGetter(object, property, descriptor.get), - owns(descriptor, 'set') && - defineSetter(object, property, descriptor.set); - } - return object; - }; - } - Object.defineProperties || - (Object.defineProperties = function (object, properties) { - for (let property in properties) - {owns(properties, property) && - Object.defineProperty(object, property, properties[property]);} - return object; - }), - Object.seal || - (Object.seal = function (object) { - return object; - }), - Object.freeze || - (Object.freeze = function (object) { - return object; - }); - try { - Object.freeze(function () {}); - } catch (exception) { - Object.freeze = (function (freezeObject) { - return function (object) { - return 'function' === typeof object ? object : freezeObject(object); - }; - }(Object.freeze)); - } - if ( - (Object.preventExtensions || - (Object.preventExtensions = function (object) { - return object; - }), - Object.isSealed || - (Object.isSealed = function () { - return !1; - }), - Object.isFrozen || - (Object.isFrozen = function () { - return !1; - }), - Object.isExtensible || - (Object.isExtensible = function (object) { - if (Object(object) === object) throw new TypeError(); - for (var name = ''; owns(object, name);) name += '?'; - object[name] = !0; - let returnValue = owns(object, name); - return delete object[name], returnValue; - }), - !Object.keys) - ) { - let hasDontEnumBug = !0, - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor', - ], - dontEnumsLength = dontEnums.length; - for (let key in { toString: null }) hasDontEnumBug = !1; - Object.keys = function (object) { - if ( - ('object' !== typeof object && 'function' !== typeof object) || - null === object - ) - {throw new TypeError('Object.keys called on a non-object');} - let keys = []; - for (let name in object) owns(object, name) && keys.push(name); - if (hasDontEnumBug) - {for (var i = 0, ii = dontEnumsLength; ii > i; i++) { - var dontEnum = dontEnums[i]; - owns(object, dontEnum) && keys.push(dontEnum); - }} - return keys; - }; - } - Date.now || - (Date.now = function () { - return new Date().getTime(); - }); - let ws = ' \nv\f\r   ᠎              \u2028\u2029'; - if (!String.prototype.trim || ws.trim()) { - ws = '[' + ws + ']'; - let trimBeginRegexp = RegExp('^' + ws + ws + '*'), - trimEndRegexp = RegExp(ws + ws + '*$'); - String.prototype.trim = function () { - return (this + '') - .replace(trimBeginRegexp, '') - .replace(trimEndRegexp, ''); - }; - } - var toObject = function (o) { - if (null == o) throw new TypeError('can\'t convert ' + o + ' to object'); - return Object(o); - }; -}); -ace.define( - 'sense_editor/mode/worker_parser', - ['require', 'exports', 'module'], - function () { - let at, // The index of the current character - ch, // The current character - annos, // annotations - escapee = { - '"': '"', - '\\': '\\', - '/': '/', - b: '\b', - f: '\f', - n: '\n', - r: '\r', - t: '\t', - }, - text, - annotate = function (type, text) { - annos.push({ type: type, text: text, at: at }); - }, - error = function (m) { - throw { - name: 'SyntaxError', - message: m, - at: at, - text: text, - }; - }, - reset = function (newAt) { - ch = text.charAt(newAt); - at = newAt + 1; - }, - next = function (c) { - if (c && c !== ch) { - error('Expected \'' + c + '\' instead of \'' + ch + '\''); - } - - ch = text.charAt(at); - at += 1; - return ch; - }, - nextUpTo = function (upTo, errorMessage) { - let currentAt = at, - i = text.indexOf(upTo, currentAt); - if (i < 0) { - error(errorMessage || 'Expected \'' + upTo + '\''); - } - reset(i + upTo.length); - return text.substring(currentAt, i); - }, - peek = function (offset) { - return text.charAt(at + offset); - }, - number = function () { - let number, - string = ''; - - if (ch === '-') { - string = '-'; - next('-'); - } - while (ch >= '0' && ch <= '9') { - string += ch; - next(); - } - if (ch === '.') { - string += '.'; - while (next() && ch >= '0' && ch <= '9') { - string += ch; - } - } - if (ch === 'e' || ch === 'E') { - string += ch; - next(); - if (ch === '-' || ch === '+') { - string += ch; - next(); - } - while (ch >= '0' && ch <= '9') { - string += ch; - next(); - } - } - number = +string; - if (isNaN(number)) { - error('Bad number'); - } else { - return number; - } - }, - string = function () { - let hex, - i, - string = '', - uffff; - - if (ch === '"') { - // If the current and the next characters are equal to "", empty string or start of triple quoted strings - if (peek(0) === '"' && peek(1) === '"') { - // literal - next('"'); - next('"'); - return nextUpTo('"""', 'failed to find closing \'"""\''); - } else { - while (next()) { - if (ch === '"') { - next(); - return string; - } else if (ch === '\\') { - next(); - if (ch === 'u') { - uffff = 0; - for (i = 0; i < 4; i += 1) { - hex = parseInt(next(), 16); - if (!isFinite(hex)) { - break; - } - uffff = uffff * 16 + hex; - } - string += String.fromCharCode(uffff); - } else if (typeof escapee[ch] === 'string') { - string += escapee[ch]; - } else { - break; - } - } else { - string += ch; - } - } - } - } - error('Bad string'); - }, - white = function () { - while (ch) { - // Skip whitespace. - while (ch && ch <= ' ') { - next(); - } - // if the current char in iteration is '#' or the char and the next char is equal to '//' - // we are on the single line comment - if (ch === '#' || ch === '/' && peek(0) === '/') { - // Until we are on the new line, skip to the next char - while (ch && ch !== '\n') { - next(); - } - } else if (ch === '/' && peek(0) === '*') { - // If the chars starts with '/*', we are on the multiline comment - next(); - next(); - while (ch && !(ch === '*' && peek(0) === '/')) { - // Until we have closing tags '*/', skip to the next char - next(); - } - if (ch) { - next(); - next(); - } - } else break; - } - }, - strictWhite = function () { - while (ch && (ch == ' ' || ch == '\t')) { - next(); - } - }, - newLine = function () { - if (ch == '\n') next(); - }, - word = function () { - switch (ch) { - case 't': - next('t'); - next('r'); - next('u'); - next('e'); - return true; - case 'f': - next('f'); - next('a'); - next('l'); - next('s'); - next('e'); - return false; - case 'n': - next('n'); - next('u'); - next('l'); - next('l'); - return null; - } - error('Unexpected \'' + ch + '\''); - }, - // parses and returns the method - method = function () { - switch (ch) { - case 'g': - next('g'); - next('e'); - next('t'); - return 'get'; - case 'G': - next('G'); - next('E'); - next('T'); - return 'GET'; - case 'h': - next('h'); - next('e'); - next('a'); - next('d'); - return 'head'; - case 'H': - next('H'); - next('E'); - next('A'); - next('D'); - return 'HEAD'; - case 'd': - next('d'); - next('e'); - next('l'); - next('e'); - next('t'); - next('e'); - return 'delete'; - case 'D': - next('D'); - next('E'); - next('L'); - next('E'); - next('T'); - next('E'); - return 'DELETE'; - case 'p': - next('p'); - switch (ch) { - case 'a': - next('a'); - next('t'); - next('c'); - next('h'); - return 'patch'; - case 'u': - next('u'); - next('t'); - return 'put'; - case 'o': - next('o'); - next('s'); - next('t'); - return 'post'; - default: - error('Unexpected \'' + ch + '\''); - } - break; - case 'P': - next('P'); - switch (ch) { - case 'A': - next('A'); - next('T'); - next('C'); - next('H'); - return 'PATCH'; - case 'U': - next('U'); - next('T'); - return 'PUT'; - case 'O': - next('O'); - next('S'); - next('T'); - return 'POST'; - default: - error('Unexpected \'' + ch + '\''); - } - break; - default: - error('Expected one of GET/POST/PUT/DELETE/HEAD/PATCH'); - } - }, - value, // Place holder for the value function. - array = function () { - const array = []; - - if (ch === '[') { - next('['); - white(); - if (ch === ']') { - next(']'); - return array; // empty array - } - while (ch) { - array.push(value()); - white(); - if (ch === ']') { - next(']'); - return array; - } - next(','); - white(); - } - } - error('Bad array'); - }, - object = function () { - let key, - object = {}; - - if (ch === '{') { - next('{'); - white(); - if (ch === '}') { - next('}'); - return object; // empty object - } - while (ch) { - key = string(); - white(); - next(':'); - if (Object.hasOwnProperty.call(object, key)) { - error('Duplicate key "' + key + '"'); - } - object[key] = value(); - white(); - if (ch === '}') { - next('}'); - return object; - } - next(','); - white(); - } - } - error('Bad object'); - }; - - value = function () { - white(); - switch (ch) { - case '{': - return object(); - case '[': - return array(); - case '"': - return string(); - case '-': - return number(); - default: - return ch >= '0' && ch <= '9' ? number() : word(); - } - }; - - let url = function () { - let url = ''; - while (ch && ch != '\n') { - url += ch; - next(); - } - if (url == '') { - error('Missing url'); - } - return url; - }, - request = function () { - white(); - method(); - strictWhite(); - url(); - strictWhite(); // advance to one new line - newLine(); - strictWhite(); - if (ch == '{') { - object(); - } - // multi doc request - strictWhite(); // advance to one new line - newLine(); - strictWhite(); - while (ch == '{') { - // another object - object(); - strictWhite(); - newLine(); - strictWhite(); - } - }, - comment = function () { - while (ch == '#') { - while (ch && ch !== '\n') { - next(); - } - white(); - } - }, - multi_request = function () { - while (ch && ch != '') { - white(); - if (!ch) { - continue; - } - try { - comment(); - white(); - if (!ch) { - continue; - } - request(); - white(); - } catch (e) { - annotate('error', e.message); - // snap - const substring = text.substr(at); - const nextMatch = substring.search(/^POST|HEAD|GET|PUT|DELETE|PATCH/m); - if (nextMatch < 1) return; - reset(at + nextMatch); - } - } - }; - - return function (source, reviver) { - let result; - - text = source; - at = 0; - annos = []; - next(); - multi_request(); - white(); - if (ch) { - annotate('error', 'Syntax error'); - } - - result = { annotations: annos }; - - return typeof reviver === 'function' - ? (function walk(holder, key) { - let k, - v, - value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - }({ '': result }, '')) - : result; - }; - } -); - -ace.define( - 'sense_editor/mode/worker', - [ - 'require', - 'exports', - 'module', - 'ace/lib/oop', - 'ace/worker/mirror', - 'sense_editor/mode/worker_parser', - ], - function (require, exports) { - const oop = require('ace/lib/oop'); - const Mirror = require('ace/worker/mirror').Mirror; - const parse = require('sense_editor/mode/worker_parser'); - - const SenseWorker = (exports.SenseWorker = function (sender) { - Mirror.call(this, sender); - this.setTimeout(200); - }); - - oop.inherits(SenseWorker, Mirror); - - (function () { - this.id = 'senseWorker'; - this.onUpdate = function () { - const value = this.doc.getValue(); - let pos, result; - try { - result = parse(value); - } catch (e) { - pos = this.charToDocumentPosition(e.at - 1); - this.sender.emit('error', { - row: pos.row, - column: pos.column, - text: e.message, - type: 'error', - }); - return; - } - for (let i = 0; i < result.annotations.length; i++) { - pos = this.charToDocumentPosition(result.annotations[i].at - 1); - result.annotations[i].row = pos.row; - result.annotations[i].column = pos.column; - } - this.sender.emit('ok', result.annotations); - }; - - this.charToDocumentPosition = function (charPos) { - let i = 0; - const len = this.doc.getLength(); - const nl = this.doc.getNewLineCharacter().length; - - if (!len) { - return { row: 0, column: 0 }; - } - - let lineStart = 0, - line; - while (i < len) { - line = this.doc.getLine(i); - const lineLength = line.length + nl; - if (lineStart + lineLength > charPos) { - return { - row: i, - column: charPos - lineStart, - }; - } - - lineStart += lineLength; - i += 1; - } - - return { - row: i - 1, - column: line.length, - }; - }; - }.call(SenseWorker.prototype)); - } -); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/output_tokenization.test.js b/src/plugins/console/public/application/models/legacy_core_editor/output_tokenization.test.js deleted file mode 100644 index e09bf06e48246..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/output_tokenization.test.js +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import './legacy_core_editor.test.mocks'; -import $ from 'jquery'; -import RowParser from '../../../lib/row_parser'; -import ace from 'brace'; -import { createReadOnlyAceEditor } from './create_readonly'; -let output; -const tokenIterator = ace.acequire('ace/token_iterator'); - -describe('Output Tokenization', () => { - beforeEach(() => { - output = createReadOnlyAceEditor(document.querySelector('#ConAppOutput')); - $(output.container).show(); - }); - - afterEach(() => { - $(output.container).hide(); - }); - - function tokensAsList() { - const iter = new tokenIterator.TokenIterator(output.getSession(), 0, 0); - const ret = []; - let t = iter.getCurrentToken(); - const parser = new RowParser(output); - if (parser.isEmptyToken(t)) { - t = parser.nextNonEmptyToken(iter); - } - while (t) { - ret.push({ value: t.value, type: t.type }); - t = parser.nextNonEmptyToken(iter); - } - - return ret; - } - - let testCount = 0; - - function tokenTest(tokenList, data) { - if (data && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - - test('Token test ' + testCount++, function (done) { - output.update(data, function () { - const tokens = tokensAsList(); - const normTokenList = []; - for (let i = 0; i < tokenList.length; i++) { - normTokenList.push({ type: tokenList[i++], value: tokenList[i] }); - } - - expect(tokens).toEqual(normTokenList); - done(); - }); - }); - } - - tokenTest( - ['warning', '#! warning', 'comment', '# GET url', 'paren.lparen', '{', 'paren.rparen', '}'], - '#! warning\n' + '# GET url\n' + '{}' - ); - - tokenTest( - [ - 'comment', - '# GET url', - 'paren.lparen', - '{', - 'variable', - '"f"', - 'punctuation.colon', - ':', - 'punctuation.start_triple_quote', - '"""', - 'multi_string', - 'raw', - 'punctuation.end_triple_quote', - '"""', - 'paren.rparen', - '}', - ], - '# GET url\n' + '{ "f": """raw""" }' - ); -}); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/smart_resize.ts b/src/plugins/console/public/application/models/legacy_core_editor/smart_resize.ts deleted file mode 100644 index c238e8c6a5da7..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/smart_resize.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { get, throttle } from 'lodash'; -import type { Editor } from 'brace'; - -// eslint-disable-next-line import/no-default-export -export default function (editor: Editor) { - const resize = editor.resize; - - const throttledResize = throttle(() => { - resize.call(editor, false); - - // Keep current top line in view when resizing to avoid losing user context - const userRow = get(throttledResize, 'topRow', 0); - if (userRow !== 0) { - editor.renderer.scrollToLine(userRow, false, false, () => {}); - } - }, 35); - return throttledResize; -} diff --git a/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js b/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js deleted file mode 100644 index fd8e12bf1d703..0000000000000 --- a/src/plugins/console/public/application/models/legacy_core_editor/theme_sense_dark.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import ace from 'brace'; - -ace.define('ace/theme/sense-dark', ['require', 'exports', 'module'], function (require, exports) { - exports.isDark = true; - exports.cssClass = 'ace-sense-dark'; - exports.cssText = - '.ace-sense-dark .ace_gutter {\ -background: #2e3236;\ -color: #bbbfc2;\ -}\ -.ace-sense-dark .ace_print-margin {\ -width: 1px;\ -background: #555651\ -}\ -.ace-sense-dark .ace_scroller {\ -background-color: #202328;\ -}\ -.ace-sense-dark .ace_content {\ -}\ -.ace-sense-dark .ace_text-layer {\ -color: #F8F8F2\ -}\ -.ace-sense-dark .ace_cursor {\ -border-left: 2px solid #F8F8F0\ -}\ -.ace-sense-dark .ace_overwrite-cursors .ace_cursor {\ -border-left: 0px;\ -border-bottom: 1px solid #F8F8F0\ -}\ -.ace-sense-dark .ace_marker-layer .ace_selection {\ -background: #222\ -}\ -.ace-sense-dark.ace_multiselect .ace_selection.ace_start {\ -box-shadow: 0 0 3px 0px #272822;\ -border-radius: 2px\ -}\ -.ace-sense-dark .ace_marker-layer .ace_step {\ -background: rgb(102, 82, 0)\ -}\ -.ace-sense-dark .ace_marker-layer .ace_bracket {\ -margin: -1px 0 0 -1px;\ -border: 1px solid #49483E\ -}\ -.ace-sense-dark .ace_marker-layer .ace_active-line {\ -background: #202020\ -}\ -.ace-sense-dark .ace_gutter-active-line {\ -background-color: #272727\ -}\ -.ace-sense-dark .ace_marker-layer .ace_selected-word {\ -border: 1px solid #49483E\ -}\ -.ace-sense-dark .ace_invisible {\ -color: #49483E\ -}\ -.ace-sense-dark .ace_entity.ace_name.ace_tag,\ -.ace-sense-dark .ace_keyword,\ -.ace-sense-dark .ace_meta,\ -.ace-sense-dark .ace_storage {\ -color: #F92672\ -}\ -.ace-sense-dark .ace_constant.ace_character,\ -.ace-sense-dark .ace_constant.ace_language,\ -.ace-sense-dark .ace_constant.ace_numeric,\ -.ace-sense-dark .ace_constant.ace_other {\ -color: #AE81FF\ -}\ -.ace-sense-dark .ace_invalid {\ -color: #F8F8F0;\ -background-color: #F92672\ -}\ -.ace-sense-dark .ace_invalid.ace_deprecated {\ -color: #F8F8F0;\ -background-color: #AE81FF\ -}\ -.ace-sense-dark .ace_support.ace_constant,\ -.ace-sense-dark .ace_support.ace_function {\ -color: #66D9EF\ -}\ -.ace-sense-dark .ace_fold {\ -background-color: #A6E22E;\ -border-color: #F8F8F2\ -}\ -.ace-sense-dark .ace_storage.ace_type,\ -.ace-sense-dark .ace_support.ace_class,\ -.ace-sense-dark .ace_support.ace_type {\ -font-style: italic;\ -color: #66D9EF\ -}\ -.ace-sense-dark .ace_entity.ace_name.ace_function,\ -.ace-sense-dark .ace_entity.ace_other.ace_attribute-name,\ -.ace-sense-dark .ace_variable {\ -color: #A6E22E\ -}\ -.ace-sense-dark .ace_variable.ace_parameter {\ -font-style: italic;\ -color: #FD971F\ -}\ -.ace-sense-dark .ace_string {\ -color: #E6DB74\ -}\ -.ace-sense-dark .ace_comment {\ -color: #629755\ -}\ -.ace-sense-dark .ace_markup.ace_underline {\ -text-decoration: underline\ -}\ -.ace-sense-dark .ace_indent-guide {\ -background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNQ11D6z7Bq1ar/ABCKBG6g04U2AAAAAElFTkSuQmCC) right repeat-y\ -}'; - - const dom = require('ace/lib/dom'); - dom.importCssString(exports.cssText, exports.cssClass); -}); diff --git a/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt b/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt deleted file mode 100644 index 517f22bd8ad6a..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt +++ /dev/null @@ -1,37 +0,0 @@ -GET _search -{ - "query": { "match_all": {} } -} - -#preceeding comment -GET _stats?level=shards - -#in between comment - -PUT index_1/type1/1 -{ - "f": 1 -} - -PUT index_1/type1/2 -{ - "f": 2 -} - -# comment - - -GET index_1/type1/1/_source?_source_include=f - -DELETE index_2 - - -POST /_sql?format=txt -{ - "query": "SELECT prenom FROM claude_index WHERE prenom = 'claude' ", - "fetch_size": 1 -} - -GET ,,/_search?pretty - -GET kbn:/api/spaces/space \ No newline at end of file diff --git a/src/plugins/console/public/application/models/sense_editor/create.ts b/src/plugins/console/public/application/models/sense_editor/create.ts deleted file mode 100644 index 9c6c3e38471d5..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/create.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { SenseEditor } from './sense_editor'; -import * as core from '../legacy_core_editor'; - -export function create(element: HTMLElement) { - const coreEditor = core.create(element); - const senseEditor = new SenseEditor(coreEditor); - - /** - * Init the editor - */ - senseEditor.highlightCurrentRequestsAndUpdateActionBar(); - return senseEditor; -} diff --git a/src/plugins/console/public/application/models/sense_editor/curl.ts b/src/plugins/console/public/application/models/sense_editor/curl.ts deleted file mode 100644 index 9080610a0e8c5..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/curl.ts +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -function detectCURLinLine(line: string) { - // returns true if text matches a curl request - return line.match(/^\s*?curl\s+(-X[A-Z]+)?\s*['"]?.*?['"]?(\s*$|\s+?-d\s*?['"])/); -} - -export function detectCURL(text: string) { - // returns true if text matches a curl request - if (!text) return false; - for (const line of text.split('\n')) { - if (detectCURLinLine(line)) { - return true; - } - } - return false; -} - -export function parseCURL(text: string) { - let state = 'NONE'; - const out = []; - let body: string[] = []; - let line = ''; - const lines = text.trim().split('\n'); - let matches; - - const EmptyLine = /^\s*$/; - const Comment = /^\s*(?:#|\/{2,})(.*)\n?$/; - const ExecutionComment = /^\s*#!/; - const ClosingSingleQuote = /^([^']*)'/; - const ClosingDoubleQuote = /^((?:[^\\"]|\\.)*)"/; - const EscapedQuotes = /^((?:[^\\"']|\\.)+)/; - - const LooksLikeCurl = /^\s*curl\s+/; - const CurlVerb = /-X ?(GET|HEAD|POST|PUT|DELETE|PATCH)/; - - const HasProtocol = /[\s"']https?:\/\//; - const CurlRequestWithProto = /[\s"']https?:\/\/[^\/ ]+\/+([^\s"']+)/; - const CurlRequestWithoutProto = /[\s"'][^\/ ]+\/+([^\s"']+)/; - const CurlData = /^.+\s(--data|-d)\s*/; - const SenseLine = /^\s*(GET|HEAD|POST|PUT|DELETE|PATCH)\s+\/?(.+)/; - - if (lines.length > 0 && ExecutionComment.test(lines[0])) { - lines.shift(); - } - - function nextLine() { - if (line.length > 0) { - return true; - } - if (lines.length === 0) { - return false; - } - line = lines.shift()!.replace(/[\r\n]+/g, '\n') + '\n'; - return true; - } - - function unescapeLastBodyEl() { - const str = body.pop()!.replace(/\\([\\"'])/g, '$1'); - body.push(str); - } - - // Is the next char a single or double quote? - // If so remove it - function detectQuote() { - if (line.substr(0, 1) === "'") { - line = line.substr(1); - state = 'SINGLE_QUOTE'; - } else if (line.substr(0, 1) === '"') { - line = line.substr(1); - state = 'DOUBLE_QUOTE'; - } else { - state = 'UNQUOTED'; - } - } - - // Body is finished - append to output with final LF - function addBodyToOut() { - if (body.length > 0) { - out.push(body.join('')); - body = []; - } - state = 'LF'; - out.push('\n'); - } - - // If the pattern matches, then the state is about to change, - // so add the capture to the body and detect the next state - // Otherwise add the whole line - function consumeMatching(pattern: string | RegExp) { - const result = line.match(pattern); - if (result) { - body.push(result[1]); - line = line.substr(result[0].length); - detectQuote(); - } else { - body.push(line); - line = ''; - } - } - - function parseCurlLine() { - let verb = 'GET'; - let request = ''; - let result; - if ((result = line.match(CurlVerb))) { - verb = result[1]; - } - - // JS regexen don't support possessive quantifiers, so - // we need two distinct patterns - const pattern = HasProtocol.test(line) ? CurlRequestWithProto : CurlRequestWithoutProto; - - if ((result = line.match(pattern))) { - request = result[1]; - } - - out.push(verb + ' /' + request + '\n'); - - if ((result = line.match(CurlData))) { - line = line.substr(result[0].length); - detectQuote(); - if (EmptyLine.test(line)) { - line = ''; - } - } else { - state = 'NONE'; - line = ''; - out.push(''); - } - } - - while (nextLine()) { - if (state === 'SINGLE_QUOTE') { - consumeMatching(ClosingSingleQuote); - } else if (state === 'DOUBLE_QUOTE') { - consumeMatching(ClosingDoubleQuote); - unescapeLastBodyEl(); - } else if (state === 'UNQUOTED') { - consumeMatching(EscapedQuotes); - if (body.length) { - unescapeLastBodyEl(); - } - if (state === 'UNQUOTED') { - addBodyToOut(); - line = ''; - } - } - - // the BODY state (used to match the body of a Sense request) - // can be terminated early if it encounters - // a comment or an empty line - else if (state === 'BODY') { - if (Comment.test(line) || EmptyLine.test(line)) { - addBodyToOut(); - } else { - body.push(line); - line = ''; - } - } else if (EmptyLine.test(line)) { - if (state !== 'LF') { - out.push('\n'); - state = 'LF'; - } - line = ''; - } else if ((matches = line.match(Comment))) { - out.push('#' + matches[1] + '\n'); - state = 'NONE'; - line = ''; - } else if (LooksLikeCurl.test(line)) { - parseCurlLine(); - } else if ((matches = line.match(SenseLine))) { - out.push(matches[1] + ' /' + matches[2] + '\n'); - line = ''; - state = 'BODY'; - } - - // Nothing else matches, so output with a prefix of ### for debugging purposes - else { - out.push('### ' + line); - line = ''; - } - } - - addBodyToOut(); - return out.join('').trim(); -} diff --git a/src/plugins/console/public/application/models/sense_editor/index.ts b/src/plugins/console/public/application/models/sense_editor/index.ts deleted file mode 100644 index 2bd44988dc02f..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './create'; -export * from '../legacy_core_editor/create_readonly'; -export { MODE } from '../../../lib/row_parser'; -export { SenseEditor } from './sense_editor'; -export { getEndpointFromPosition } from '../../../lib/autocomplete/get_endpoint_from_position'; diff --git a/src/plugins/console/public/application/models/sense_editor/integration.test.js b/src/plugins/console/public/application/models/sense_editor/integration.test.js deleted file mode 100644 index bed83293e31d6..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/integration.test.js +++ /dev/null @@ -1,1279 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import './sense_editor.test.mocks'; -import { create } from './create'; -import _ from 'lodash'; -import $ from 'jquery'; - -import * as kb from '../../../lib/kb/kb'; -import { AutocompleteInfo, setAutocompleteInfo } from '../../../services'; -import { httpServiceMock } from '@kbn/core-http-browser-mocks'; -import { StorageMock } from '../../../services/storage.mock'; -import { SettingsMock } from '../../../services/settings.mock'; - -describe('Integration', () => { - let senseEditor; - let autocompleteInfo; - - beforeEach(() => { - // Set up our document body - document.body.innerHTML = - '
'; - - senseEditor = create(document.querySelector('#ConAppEditor')); - $(senseEditor.getCoreEditor().getContainer()).show(); - senseEditor.autocomplete._test.removeChangeListener(); - autocompleteInfo = new AutocompleteInfo(); - - const httpMock = httpServiceMock.createSetupContract(); - const storage = new StorageMock({}, 'test'); - const settingsMock = new SettingsMock(storage); - - settingsMock.getAutocomplete.mockReturnValue({ fields: true }); - - autocompleteInfo.mapping.setup(httpMock, settingsMock); - - setAutocompleteInfo(autocompleteInfo); - }); - afterEach(() => { - $(senseEditor.getCoreEditor().getContainer()).hide(); - senseEditor.autocomplete._test.addChangeListener(); - autocompleteInfo = null; - setAutocompleteInfo(null); - }); - - function processContextTest(data, mapping, kbSchemes, requestLine, testToRun) { - test(testToRun.name, function (done) { - let lineOffset = 0; // add one for the extra method line - let editorValue = data; - if (requestLine != null) { - if (data != null) { - editorValue = requestLine + '\n' + data; - lineOffset = 1; - } else { - editorValue = requestLine; - } - } - - testToRun.cursor.lineNumber += lineOffset; - - autocompleteInfo.clear(); - autocompleteInfo.mapping.loadMappings(mapping); - const json = {}; - json[test.name] = kbSchemes || {}; - const testApi = kb._test.loadApisFromJson(json); - if (kbSchemes) { - // if (kbSchemes.globals) { - // $.each(kbSchemes.globals, function (parent, rules) { - // testApi.addGlobalAutocompleteRules(parent, rules); - // }); - // } - if (kbSchemes.endpoints) { - $.each(kbSchemes.endpoints, function (endpoint, scheme) { - testApi.addEndpointDescription(endpoint, scheme); - }); - } - } - kb._test.setActiveApi(testApi); - const { cursor } = testToRun; - senseEditor.update(editorValue, true).then(() => { - senseEditor.getCoreEditor().moveCursorToPosition(cursor); - // allow ace rendering to move cursor so it will be seen during test - handy for debugging. - //setTimeout(function () { - senseEditor.completer = { - base: {}, - changeListener: function () {}, - }; // mimic auto complete - - senseEditor.autocomplete._test.getCompletions( - senseEditor, - null, - cursor, - '', - function (err, terms) { - if (testToRun.assertThrows) { - done(); - return; - } - - if (err) { - throw err; - } - - if (testToRun.no_context) { - expect(!terms || terms.length === 0).toBeTruthy(); - } else { - expect(terms).not.toBeNull(); - expect(terms.length).toBeGreaterThan(0); - } - - if (!terms || terms.length === 0) { - done(); - return; - } - - if (testToRun.autoCompleteSet) { - const expectedTerms = _.map(testToRun.autoCompleteSet, function (t) { - if (typeof t !== 'object') { - t = { name: t }; - } - return t; - }); - if (terms.length !== expectedTerms.length) { - expect(_.map(terms, 'name')).toEqual(_.map(expectedTerms, 'name')); - } else { - const filteredActualTerms = _.map(terms, function (actualTerm, i) { - const expectedTerm = expectedTerms[i]; - const filteredTerm = {}; - _.each(expectedTerm, function (v, p) { - filteredTerm[p] = actualTerm[p]; - }); - return filteredTerm; - }); - expect(filteredActualTerms).toEqual(expectedTerms); - } - } - - const context = terms[0].context; - const { - cursor: { lineNumber, column }, - } = testToRun; - senseEditor.autocomplete._test.addReplacementInfoToContext( - context, - { lineNumber, column }, - terms[0].value - ); - - function ac(prop, propTest) { - if (typeof testToRun[prop] !== 'undefined') { - if (propTest) { - propTest(context[prop], testToRun[prop], prop); - } else { - expect(context[prop]).toEqual(testToRun[prop]); - } - } - } - - function posCompare(actual, expected) { - expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); - expect(actual.column).toEqual(expected.column); - } - - function rangeCompare(actual, expected, name) { - posCompare(actual.start, expected.start, name + '.start'); - posCompare(actual.end, expected.end, name + '.end'); - } - - ac('prefixToAdd'); - ac('suffixToAdd'); - ac('addTemplate'); - ac('textBoxPosition', posCompare); - ac('rangeToReplace', rangeCompare); - done(); - }, - { setAnnotation: () => {}, removeAnnotation: () => {} } - ); - }); - }); - } - - function contextTests(data, mapping, kbSchemes, requestLine, tests) { - if (data != null && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - for (let t = 0; t < tests.length; t++) { - processContextTest(data, mapping, kbSchemes, requestLine, tests[t]); - } - } - - const SEARCH_KB = { - endpoints: { - _search: { - methods: ['GET', 'POST'], - patterns: ['{index}/_search', '_search'], - data_autocomplete_rules: { - query: { - match_all: {}, - term: { '{field}': { __template: { f: 1 } } }, - }, - size: {}, - facets: { - __template: { - FIELD: {}, - }, - '*': { terms: { field: '{field}' } }, - }, - }, - }, - }, - }; - - const MAPPING = { - index1: { - properties: { - 'field1.1.1': { type: 'string' }, - 'field1.1.2': { type: 'string' }, - }, - }, - index2: { - properties: { - 'field2.1.1': { type: 'string' }, - 'field2.1.2': { type: 'string' }, - }, - }, - }; - - contextTests({}, MAPPING, SEARCH_KB, 'POST _search', [ - { - name: 'Empty doc', - cursor: { lineNumber: 1, column: 2 }, - initialValue: '', - addTemplate: true, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 1, column: 2 }, - end: { lineNumber: 1, column: 2 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - ]); - - contextTests({}, MAPPING, SEARCH_KB, 'POST _no_context', [ - { - name: 'Missing KB', - cursor: { lineNumber: 1, column: 2 }, - no_context: true, - }, - ]); - - contextTests( - { - query: { - f: 1, - }, - }, - MAPPING, - { - globals: { - query: { - t1: 2, - }, - }, - endpoints: {}, - }, - 'POST _no_context', - [ - { - name: 'Missing KB - global auto complete', - cursor: { lineNumber: 3, column: 6 }, - autoCompleteSet: ['t1'], - }, - ] - ); - - contextTests( - { - query: { - field: 'something', - }, - facets: {}, - size: 20, - }, - MAPPING, - SEARCH_KB, - 'POST _search', - [ - { - name: 'existing dictionary key, no template', - cursor: { lineNumber: 2, column: 6 }, - initialValue: 'query', - addTemplate: false, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 2, column: 4 }, - end: { lineNumber: 2, column: 11 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - { - name: 'existing inner dictionary key', - cursor: { lineNumber: 3, column: 8 }, - initialValue: 'field', - addTemplate: false, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 3, column: 7 }, - end: { lineNumber: 3, column: 14 }, - }, - autoCompleteSet: ['match_all', 'term'], - }, - { - name: 'existing dictionary key, yes template', - cursor: { lineNumber: 5, column: 8 }, - initialValue: 'facets', - addTemplate: true, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 5, column: 4 }, - end: { lineNumber: 5, column: 16 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - { - name: 'ignoring meta keys', - cursor: { lineNumber: 5, column: 15 }, - no_context: true, - }, - ] - ); - - contextTests( - '{\n' + - ' "query": {\n' + - ' "field": "something"\n' + - ' },\n' + - ' "facets": {},\n' + - ' "size": 20\n' + - '}', - MAPPING, - SEARCH_KB, - 'POST _search', - [ - { - name: 'trailing comma, end of line', - cursor: { lineNumber: 5, column: 17 }, - initialValue: '', - addTemplate: true, - prefixToAdd: '', - suffixToAdd: ', ', - rangeToReplace: { - start: { lineNumber: 5, column: 17 }, - end: { lineNumber: 5, column: 17 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - { - name: 'trailing comma, beginning of line', - cursor: { lineNumber: 6, column: 2 }, - initialValue: '', - addTemplate: true, - prefixToAdd: '', - suffixToAdd: ', ', - rangeToReplace: { - start: { lineNumber: 6, column: 2 }, - end: { lineNumber: 6, column: 2 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - { - name: 'prefix comma, end of line', - cursor: { lineNumber: 7, column: 1 }, - initialValue: '', - addTemplate: true, - prefixToAdd: ',\n', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 6, column: 14 }, - end: { lineNumber: 7, column: 1 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - ] - ); - - contextTests( - { - object: 1, - array: 1, - value_one_of: 1, - value: 2, - something_else: 5, - }, - MAPPING, - { - endpoints: { - _test: { - patterns: ['_test'], - data_autocomplete_rules: { - object: { bla: 1 }, - array: [1], - value_one_of: { __one_of: [1, 2] }, - value: 3, - '*': { __one_of: [4, 5] }, - }, - }, - }, - }, - 'GET _test', - [ - { - name: 'not matching object when { is not opened', - cursor: { lineNumber: 2, column: 13 }, - initialValue: '', - autoCompleteSet: ['{'], - }, - { - name: 'not matching array when [ is not opened', - cursor: { lineNumber: 3, column: 13 }, - initialValue: '', - autoCompleteSet: ['['], - }, - { - name: 'matching value with one_of', - cursor: { lineNumber: 4, column: 20 }, - initialValue: '', - autoCompleteSet: [1, 2], - }, - { - name: 'matching value', - cursor: { lineNumber: 5, column: 13 }, - initialValue: '', - autoCompleteSet: [3], - }, - { - name: 'matching any value with one_of', - cursor: { lineNumber: 6, column: 22 }, - initialValue: '', - autoCompleteSet: [4, 5], - }, - ] - ); - - contextTests( - { - query: { - field: 'something', - }, - facets: { - name: {}, - }, - size: 20, - }, - MAPPING, - SEARCH_KB, - 'GET _search', - [ - { - name: '* matching everything', - cursor: { lineNumber: 6, column: 16 }, - initialValue: '', - addTemplate: true, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 6, column: 16 }, - end: { lineNumber: 6, column: 16 }, - }, - autoCompleteSet: [{ name: 'terms', meta: 'API' }], - }, - ] - ); - - contextTests( - { - index: '123', - }, - MAPPING, - { - endpoints: { - _test: { - patterns: ['_test'], - data_autocomplete_rules: { - index: '{index}', - }, - }, - }, - }, - 'GET _test', - [ - { - name: '{index} matching', - cursor: { lineNumber: 2, column: 16 }, - autoCompleteSet: [ - { name: 'index1', meta: 'index' }, - { name: 'index2', meta: 'index' }, - ], - }, - ] - ); - - function tt(term, template, meta) { - term = { name: term, template: template }; - if (meta) { - term.meta = meta; - } - return term; - } - - contextTests( - { - array: ['a'], - oneof: '1', - }, - MAPPING, - { - endpoints: { - _endpoint: { - patterns: ['_endpoint'], - data_autocomplete_rules: { - array: ['a', 'b'], - number: 1, - object: {}, - fixed: { __template: { a: 1 } }, - oneof: { __one_of: ['o1', 'o2'] }, - }, - }, - }, - }, - 'GET _endpoint', - [ - { - name: 'Templates 1', - cursor: { lineNumber: 2, column: 1 }, - autoCompleteSet: [ - tt('array', []), - tt('fixed', { a: 1 }), - tt('number', 1), - tt('object', {}), - tt('oneof', 'o1'), - ], - }, - { - name: 'Templates - one off', - cursor: { lineNumber: 5, column: 13 }, - autoCompleteSet: [tt('o1'), tt('o2')], - }, - ] - ); - - contextTests( - { - string: 'value', - context: {}, - }, - MAPPING, - { - endpoints: { - _endpoint: { - patterns: ['_endpoint'], - data_autocomplete_rules: { - context: { - __one_of: [ - { - __condition: { - lines_regex: 'value', - }, - match: {}, - }, - { - __condition: { - lines_regex: 'other', - }, - no_match: {}, - }, - { always: {} }, - ], - }, - }, - }, - }, - }, - 'GET _endpoint', - [ - { - name: 'Conditionals', - cursor: { lineNumber: 3, column: 16 }, - autoCompleteSet: [tt('always', {}), tt('match', {})], - }, - ] - ); - - contextTests( - { - any_of_numbers: [1], - any_of_obj: [ - { - a: 1, - }, - ], - any_of_mixed: [ - { - a: 1, - }, - 2, - ], - }, - MAPPING, - { - endpoints: { - _endpoint: { - patterns: ['_endpoint'], - data_autocomplete_rules: { - any_of_numbers: { __template: [1, 2], __any_of: [1, 2, 3] }, - any_of_obj: { - __template: [{ c: 1 }], - __any_of: [{ a: 1, b: 2 }, { c: 1 }], - }, - any_of_mixed: { - __any_of: [{ a: 1 }, 3], - }, - }, - }, - }, - }, - 'GET _endpoint', - [ - { - name: 'Any of - templates', - cursor: { lineNumber: 2, column: 1 }, - autoCompleteSet: [ - tt('any_of_mixed', []), - tt('any_of_numbers', [1, 2]), - tt('any_of_obj', [{ c: 1 }]), - ], - }, - { - name: 'Any of - numbers', - cursor: { lineNumber: 3, column: 3 }, - autoCompleteSet: [1, 2, 3], - }, - { - name: 'Any of - object', - cursor: { lineNumber: 7, column: 3 }, - autoCompleteSet: [tt('a', 1), tt('b', 2), tt('c', 1)], - }, - { - name: 'Any of - mixed - obj', - cursor: { lineNumber: 12, column: 3 }, - autoCompleteSet: [tt('a', 1)], - }, - { - name: 'Any of - mixed - both', - cursor: { lineNumber: 14, column: 3 }, - autoCompleteSet: [tt(3), tt('{')], - }, - ] - ); - - contextTests( - {}, - MAPPING, - { - endpoints: { - _endpoint: { - patterns: ['_endpoint'], - data_autocomplete_rules: { - query: '', - }, - }, - }, - }, - 'GET _endpoint', - [ - { - name: 'Empty string as default', - cursor: { lineNumber: 1, column: 2 }, - autoCompleteSet: [tt('query', '')], - }, - ] - ); - - // NOTE: This test emits "error while getting completion terms Error: failed to resolve link - // [GLOBAL.broken]: Error: failed to resolve global components for ['broken']". but that's - // expected. - contextTests( - { - a: { - b: {}, - c: {}, - d: { - t1a: {}, - }, - e: {}, - f: [{}], - g: {}, - h: {}, - }, - }, - MAPPING, - { - globals: { - gtarget: { - t1: 2, - t1a: { - __scope_link: '.', - }, - }, - }, - endpoints: { - _current: { - patterns: ['_current'], - data_autocomplete_rules: { - a: { - b: { - __scope_link: '.a', - }, - c: { - __scope_link: 'ext.target', - }, - d: { - __scope_link: 'GLOBAL.gtarget', - }, - e: { - __scope_link: 'ext', - }, - f: [ - { - __scope_link: 'ext.target', - }, - ], - g: { - __scope_link: function () { - return { - a: 1, - b: 2, - }; - }, - }, - h: { - __scope_link: 'GLOBAL.broken', - }, - }, - }, - }, - ext: { - patterns: ['ext'], - data_autocomplete_rules: { - target: { - t2: 1, - }, - }, - }, - }, - }, - 'GET _current', - [ - { - name: 'Relative scope link test', - cursor: { lineNumber: 3, column: 13 }, - autoCompleteSet: [ - tt('b', {}), - tt('c', {}), - tt('d', {}), - tt('e', {}), - tt('f', [{}]), - tt('g', {}), - tt('h', {}), - ], - }, - { - name: 'External scope link test', - cursor: { lineNumber: 4, column: 13 }, - autoCompleteSet: [tt('t2', 1)], - }, - { - name: 'Global scope link test', - cursor: { lineNumber: 5, column: 13 }, - autoCompleteSet: [tt('t1', 2), tt('t1a', {})], - }, - { - name: 'Global scope link with an internal scope link', - cursor: { lineNumber: 6, column: 18 }, - autoCompleteSet: [tt('t1', 2), tt('t1a', {})], - }, - { - name: 'Entire endpoint scope link test', - cursor: { lineNumber: 8, column: 13 }, - autoCompleteSet: [tt('target', {})], - }, - { - name: 'A scope link within an array', - cursor: { lineNumber: 10, column: 11 }, - autoCompleteSet: [tt('t2', 1)], - }, - { - name: 'A function based scope link', - cursor: { lineNumber: 12, column: 13 }, - autoCompleteSet: [tt('a', 1), tt('b', 2)], - }, - { - name: 'A global scope link with wrong link', - cursor: { lineNumber: 13, column: 13 }, - assertThrows: /broken/, - }, - ] - ); - - contextTests( - {}, - MAPPING, - { - globals: { - gtarget: { - t1: 2, - }, - }, - endpoints: { - _current: { - patterns: ['_current'], - id: 'GET _current', - data_autocomplete_rules: { - __scope_link: 'GLOBAL.gtarget', - }, - }, - }, - }, - 'GET _current', - [ - { - name: 'Top level scope link', - cursor: { lineNumber: 1, column: 2 }, - autoCompleteSet: [tt('t1', 2)], - }, - ] - ); - - contextTests( - { - a: {}, - }, - MAPPING, - { - endpoints: { - _endpoint: { - patterns: ['_endpoint'], - data_autocomplete_rules: { - a: {}, - b: {}, - }, - }, - }, - }, - 'GET _endpoint', - [ - { - name: 'Path after empty object', - cursor: { lineNumber: 2, column: 11 }, - autoCompleteSet: ['a', 'b'], - }, - ] - ); - - contextTests( - { - '': {}, - }, - MAPPING, - SEARCH_KB, - 'POST _search', - [ - { - name: 'Replace an empty string', - cursor: { lineNumber: 2, column: 5 }, - rangeToReplace: { - start: { lineNumber: 2, column: 4 }, - end: { lineNumber: 2, column: 10 }, - }, - }, - ] - ); - - contextTests( - { - a: [ - { - c: {}, - }, - ], - }, - MAPPING, - { - endpoints: { - _endpoint: { - patterns: ['_endpoint'], - data_autocomplete_rules: { - a: [{ b: 1 }], - }, - }, - }, - }, - 'GET _endpoint', - [ - { - name: 'List of objects - internal autocomplete', - cursor: { lineNumber: 4, column: 11 }, - autoCompleteSet: ['b'], - }, - { - name: 'List of objects - external template', - cursor: { lineNumber: 1, column: 2 }, - autoCompleteSet: [tt('a', [{}])], - }, - ] - ); - - contextTests( - { - query: { - term: { - field: 'something', - }, - }, - facets: { - test: { - terms: { - field: 'test', - }, - }, - }, - size: 20, - }, - MAPPING, - SEARCH_KB, - 'POST index1/_search', - [ - { - name: 'Field completion as scope', - cursor: { lineNumber: 4, column: 11 }, - autoCompleteSet: [ - tt('field1.1.1', { f: 1 }, 'string'), - tt('field1.1.2', { f: 1 }, 'string'), - ], - }, - { - name: 'Field completion as value', - cursor: { lineNumber: 10, column: 24 }, - autoCompleteSet: [ - { name: 'field1.1.1', meta: 'string' }, - { name: 'field1.1.2', meta: 'string' }, - ], - }, - ] - ); - - // NOTE: This test emits "Can't extract a valid url token path", but that's expected. - contextTests('POST _search\n', MAPPING, SEARCH_KB, null, [ - { - name: 'initial doc start', - cursor: { lineNumber: 2, column: 1 }, - autoCompleteSet: ['{'], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests( - '{\n' + ' "query": {} \n' + '}\n' + '\n' + '\n', - MAPPING, - SEARCH_KB, - 'POST _search', - [ - { - name: 'Cursor rows after request end', - cursor: { lineNumber: 5, column: 1 }, - autoCompleteSet: ['GET', 'PUT', 'POST', 'DELETE', 'HEAD', 'PATCH'], - prefixToAdd: '', - suffixToAdd: ' ', - }, - { - name: 'Cursor just after request end', - cursor: { lineNumber: 3, column: 2 }, - no_context: true, - }, - ] - ); - - const CLUSTER_KB = { - endpoints: { - _search: { - patterns: ['_search', '{index}/_search'], - url_params: { - search_type: ['count', 'query_then_fetch'], - scroll: '10m', - }, - methods: ['GET'], - data_autocomplete_rules: {}, - }, - '_cluster/stats': { - patterns: ['_cluster/stats'], - indices_mode: 'none', - data_autocomplete_rules: {}, - methods: ['GET'], - }, - '_cluster/nodes/stats': { - patterns: ['_cluster/nodes/stats'], - data_autocomplete_rules: {}, - methods: ['GET'], - }, - }, - }; - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _cluster', [ - { - name: 'Endpoints with slashes - no slash', - cursor: { lineNumber: 1, column: 9 }, - autoCompleteSet: ['_cluster/nodes/stats', '_cluster/stats', '_search', 'index1', 'index2'], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _cluster/', [ - { - name: 'Endpoints with slashes - before slash', - cursor: { lineNumber: 1, column: 8 }, - autoCompleteSet: ['_cluster/nodes/stats', '_cluster/stats', '_search', 'index1', 'index2'], - prefixToAdd: '', - suffixToAdd: '', - }, - { - name: 'Endpoints with slashes - on slash', - cursor: { lineNumber: 1, column: 13 }, - autoCompleteSet: ['_cluster/nodes/stats', '_cluster/stats', '_search', 'index1', 'index2'], - prefixToAdd: '', - suffixToAdd: '', - }, - { - name: 'Endpoints with slashes - after slash', - cursor: { lineNumber: 1, column: 14 }, - autoCompleteSet: ['nodes/stats', 'stats'], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _cluster/no', [ - { - name: 'Endpoints with slashes - after slash', - cursor: { lineNumber: 1, column: 15 }, - autoCompleteSet: [ - { name: 'nodes/stats', meta: 'endpoint' }, - { name: 'stats', meta: 'endpoint' }, - ], - prefixToAdd: '', - suffixToAdd: '', - initialValue: 'no', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _cluster/nodes/st', [ - { - name: 'Endpoints with two slashes', - cursor: { lineNumber: 1, column: 21 }, - autoCompleteSet: ['stats'], - prefixToAdd: '', - suffixToAdd: '', - initialValue: 'st', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET ', [ - { - name: 'Immediately after space + method', - cursor: { lineNumber: 1, column: 5 }, - autoCompleteSet: [ - { name: '_cluster/nodes/stats', meta: 'endpoint' }, - { name: '_cluster/stats', meta: 'endpoint' }, - { name: '_search', meta: 'endpoint' }, - { name: 'index1', meta: 'index' }, - { name: 'index2', meta: 'index' }, - ], - prefixToAdd: '', - suffixToAdd: '', - initialValue: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET cl', [ - { - name: 'Endpoints by subpart GET', - cursor: { lineNumber: 1, column: 7 }, - autoCompleteSet: [ - { name: '_cluster/nodes/stats', meta: 'endpoint' }, - { name: '_cluster/stats', meta: 'endpoint' }, - { name: '_search', meta: 'endpoint' }, - { name: 'index1', meta: 'index' }, - { name: 'index2', meta: 'index' }, - ], - prefixToAdd: '', - suffixToAdd: '', - initialValue: 'cl', - method: 'GET', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'POST cl', [ - { - name: 'Endpoints by subpart POST', - cursor: { lineNumber: 1, column: 8 }, - no_context: true, - prefixToAdd: '', - suffixToAdd: '', - initialValue: 'cl', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _search?', [ - { - name: 'Params just after ?', - cursor: { lineNumber: 1, column: 13 }, - autoCompleteSet: [ - { name: 'filter_path', meta: 'param', insertValue: 'filter_path=' }, - { name: 'format', meta: 'param', insertValue: 'format=' }, - { name: 'pretty', meta: 'flag' }, - { name: 'scroll', meta: 'param', insertValue: 'scroll=' }, - { name: 'search_type', meta: 'param', insertValue: 'search_type=' }, - ], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _search?format=', [ - { - name: 'Params values', - cursor: { lineNumber: 1, column: 20 }, - autoCompleteSet: [ - { name: 'json', meta: 'format' }, - { name: 'yaml', meta: 'format' }, - ], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _search?format=yaml&', [ - { - name: 'Params after amp', - cursor: { lineNumber: 1, column: 25 }, - autoCompleteSet: [ - { name: 'filter_path', meta: 'param', insertValue: 'filter_path=' }, - { name: 'format', meta: 'param', insertValue: 'format=' }, - { name: 'pretty', meta: 'flag' }, - { name: 'scroll', meta: 'param', insertValue: 'scroll=' }, - { name: 'search_type', meta: 'param', insertValue: 'search_type=' }, - ], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _search?format=yaml&search', [ - { - name: 'Params on existing param', - cursor: { lineNumber: 1, column: 27 }, - rangeToReplace: { - start: { lineNumber: 1, column: 25 }, - end: { lineNumber: 1, column: 31 }, - }, - autoCompleteSet: [ - { name: 'filter_path', meta: 'param', insertValue: 'filter_path=' }, - { name: 'format', meta: 'param', insertValue: 'format=' }, - { name: 'pretty', meta: 'flag' }, - { name: 'scroll', meta: 'param', insertValue: 'scroll=' }, - { name: 'search_type', meta: 'param', insertValue: 'search_type=' }, - ], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _search?format=yaml&search_type=cou', [ - { - name: 'Params on existing value', - cursor: { lineNumber: 1, column: 38 }, - rangeToReplace: { - start: { lineNumber: 1, column: 37 }, - end: { lineNumber: 1, column: 40 }, - }, - autoCompleteSet: [ - { name: 'count', meta: 'search_type' }, - { name: 'query_then_fetch', meta: 'search_type' }, - ], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests(null, MAPPING, CLUSTER_KB, 'GET _search?format=yaml&search_type=cou', [ - { - name: 'Params on just after = with existing value', - cursor: { lineNumber: 1, column: 37 }, - rangeToReplace: { - start: { lineNumber: 1, column: 37 }, - end: { lineNumber: 1, column: 37 }, - }, - autoCompleteSet: [ - { name: 'count', meta: 'search_type' }, - { name: 'query_then_fetch', meta: 'search_type' }, - ], - prefixToAdd: '', - suffixToAdd: '', - }, - ]); - - contextTests( - { - query: { - field: 'something', - }, - facets: {}, - size: 20, - }, - MAPPING, - SEARCH_KB, - 'POST http://somehost/_search', - [ - { - name: 'fullurl - existing dictionary key, no template', - cursor: { lineNumber: 2, column: 7 }, - initialValue: 'query', - addTemplate: false, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 2, column: 4 }, - end: { lineNumber: 2, column: 11 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - { - name: 'fullurl - existing inner dictionary key', - cursor: { lineNumber: 3, column: 8 }, - initialValue: 'field', - addTemplate: false, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 3, column: 7 }, - end: { lineNumber: 3, column: 14 }, - }, - autoCompleteSet: ['match_all', 'term'], - }, - { - name: 'fullurl - existing dictionary key, yes template', - cursor: { lineNumber: 5, column: 8 }, - initialValue: 'facets', - addTemplate: true, - prefixToAdd: '', - suffixToAdd: '', - rangeToReplace: { - start: { lineNumber: 5, column: 4 }, - end: { lineNumber: 5, column: 16 }, - }, - autoCompleteSet: ['facets', 'query', 'size'], - }, - ] - ); -}); diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js deleted file mode 100644 index 19d782f1b8e87..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js +++ /dev/null @@ -1,641 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import './sense_editor.test.mocks'; - -import $ from 'jquery'; -import _ from 'lodash'; -import { URL } from 'url'; - -import { create } from './create'; -import { XJson } from '@kbn/es-ui-shared-plugin/public'; -import editorInput1 from './__fixtures__/editor_input1.txt'; -import { setStorage, createStorage } from '../../../services'; - -const { collapseLiteralStrings } = XJson; - -describe('Editor', () => { - let input; - let oldUrl; - let olldWindow; - let storage; - - beforeEach(function () { - // Set up our document body - document.body.innerHTML = `
-
-
-
-
`; - - input = create(document.querySelector('#ConAppEditor')); - $(input.getCoreEditor().getContainer()).show(); - input.autocomplete._test.removeChangeListener(); - oldUrl = global.URL; - olldWindow = { ...global.window }; - global.URL = URL; - Object.defineProperty(global, 'window', { - value: Object.create(window), - writable: true, - }); - Object.defineProperty(window, 'location', { - value: { - origin: 'http://localhost:5620', - }, - }); - storage = createStorage({ - engine: global.window.localStorage, - prefix: 'console_test', - }); - setStorage(storage); - }); - afterEach(function () { - global.URL = oldUrl; - global.window = olldWindow; - $(input.getCoreEditor().getContainer()).hide(); - input.autocomplete._test.addChangeListener(); - setStorage(null); - }); - - let testCount = 0; - - const callWithEditorMethod = (editorMethod, fn) => async (done) => { - const results = await input[editorMethod](); - fn(results, done); - }; - - function utilsTest(name, prefix, data, testToRun) { - const id = testCount++; - if (typeof data === 'function') { - testToRun = data; - data = null; - } - if (data && typeof data !== 'string') { - data = JSON.stringify(data, null, 3); - } - if (data) { - if (prefix) { - data = prefix + '\n' + data; - } - } else { - data = prefix; - } - - test('Utils test ' + id + ' : ' + name, function (done) { - input.update(data, true).then(() => { - testToRun(done); - }); - }); - } - - function compareRequest(requests, expected) { - if (!Array.isArray(requests)) { - requests = [requests]; - expected = [expected]; - } - - _.each(requests, function (r) { - delete r.range; - }); - expect(requests).toEqual(expected); - } - - const simpleRequest = { - prefix: 'POST _search', - data: ['{', ' "query": { "match_all": {} }', '}'].join('\n'), - }; - - const singleLineRequest = { - prefix: 'POST _search', - data: '{ "query": { "match_all": {} } }', - }; - - const getRequestNoData = { - prefix: 'GET _stats', - }; - - const multiDocRequest = { - prefix: 'POST _bulk', - data_as_array: ['{ "index": { "_index": "index", "_type":"type" } }', '{ "field": 1 }'], - }; - multiDocRequest.data = multiDocRequest.data_as_array.join('\n'); - - utilsTest( - 'simple request range', - simpleRequest.prefix, - simpleRequest.data, - callWithEditorMethod('getRequestRange', (range, done) => { - compareRequest(range, { - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 4, column: 2 }, - }); - done(); - }) - ); - - utilsTest( - 'simple request data', - simpleRequest.prefix, - simpleRequest.data, - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'POST', - url: '_search', - data: [simpleRequest.data], - }; - compareRequest(request, expected); - done(); - }) - ); - - utilsTest( - 'simple request range, prefixed with spaces', - ' ' + simpleRequest.prefix, - simpleRequest.data, - callWithEditorMethod('getRequestRange', (range, done) => { - expect(range).toEqual({ - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 4, column: 2 }, - }); - done(); - }) - ); - - utilsTest( - 'simple request data, prefixed with spaces', - ' ' + simpleRequest.prefix, - simpleRequest.data, - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'POST', - url: '_search', - data: [simpleRequest.data], - }; - - compareRequest(request, expected); - done(); - }) - ); - - utilsTest( - 'simple request range, suffixed with spaces', - simpleRequest.prefix + ' ', - simpleRequest.data + ' ', - callWithEditorMethod('getRequestRange', (range, done) => { - compareRequest(range, { - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 4, column: 2 }, - }); - done(); - }) - ); - - utilsTest( - 'simple request data, suffixed with spaces', - simpleRequest.prefix + ' ', - simpleRequest.data + ' ', - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'POST', - url: '_search', - data: [simpleRequest.data], - }; - - compareRequest(request, expected); - done(); - }) - ); - - utilsTest( - 'single line request range', - singleLineRequest.prefix, - singleLineRequest.data, - callWithEditorMethod('getRequestRange', (range, done) => { - compareRequest(range, { - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 2, column: 33 }, - }); - done(); - }) - ); - - utilsTest( - 'full url: single line request data', - 'POST https://somehost/_search', - singleLineRequest.data, - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'POST', - url: 'https://somehost/_search', - data: [singleLineRequest.data], - }; - compareRequest(request, expected); - done(); - }) - ); - - utilsTest( - 'request with no data followed by a new line', - getRequestNoData.prefix, - '\n', - callWithEditorMethod('getRequestRange', (range, done) => { - compareRequest(range, { - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 1, column: 11 }, - }); - done(); - }) - ); - - utilsTest( - 'request with no data followed by a new line (data)', - getRequestNoData.prefix, - '\n', - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'GET', - url: '_stats', - data: [], - }; - compareRequest(request, expected); - done(); - }) - ); - - utilsTest( - 'request with no data', - getRequestNoData.prefix, - getRequestNoData.data, - callWithEditorMethod('getRequestRange', (range, done) => { - expect(range).toEqual({ - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 1, column: 11 }, - }); - done(); - }) - ); - - utilsTest( - 'request with no data (data)', - getRequestNoData.prefix, - getRequestNoData.data, - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'GET', - url: '_stats', - data: [], - }; - compareRequest(request, expected); - done(); - }) - ); - - utilsTest( - 'multi doc request range', - multiDocRequest.prefix, - multiDocRequest.data, - callWithEditorMethod('getRequestRange', (range, done) => { - expect(range).toEqual({ - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 3, column: 15 }, - }); - done(); - }) - ); - - utilsTest( - 'multi doc request data', - multiDocRequest.prefix, - multiDocRequest.data, - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'POST', - url: '_bulk', - data: multiDocRequest.data_as_array, - }; - compareRequest(request, expected); - done(); - }) - ); - - const scriptRequest = { - prefix: 'POST _search', - data: ['{', ' "query": { "script": """', ' some script ', ' """}', '}'].join('\n'), - }; - - utilsTest( - 'script request range', - scriptRequest.prefix, - scriptRequest.data, - callWithEditorMethod('getRequestRange', (range, done) => { - compareRequest(range, { - start: { lineNumber: 1, column: 1 }, - end: { lineNumber: 6, column: 2 }, - }); - done(); - }) - ); - - utilsTest( - 'simple request data', - simpleRequest.prefix, - simpleRequest.data, - callWithEditorMethod('getRequest', (request, done) => { - const expected = { - method: 'POST', - url: '_search', - data: [collapseLiteralStrings(simpleRequest.data)], - }; - - compareRequest(request, expected); - done(); - }) - ); - - function multiReqTest(name, editorInput, range, expected) { - utilsTest('multi request select - ' + name, editorInput, async function (done) { - const requests = await input.getRequestsInRange(range, false); - // convert to format returned by request. - _.each(expected, function (req) { - req.data = req.data == null ? [] : [JSON.stringify(req.data, null, 2)]; - }); - - compareRequest(requests, expected); - done(); - }); - } - - multiReqTest( - 'mid body to mid body', - editorInput1, - { start: { lineNumber: 13 }, end: { lineNumber: 18 } }, - [ - { - method: 'PUT', - url: 'index_1/type1/1', - data: { - f: 1, - }, - }, - { - method: 'PUT', - url: 'index_1/type1/2', - data: { - f: 2, - }, - }, - ] - ); - - multiReqTest( - 'single request start to end', - editorInput1, - { start: { lineNumber: 11 }, end: { lineNumber: 14 } }, - [ - { - method: 'PUT', - url: 'index_1/type1/1', - data: { - f: 1, - }, - }, - ] - ); - - multiReqTest( - 'start to end, with comment', - editorInput1, - { start: { lineNumber: 7 }, end: { lineNumber: 14 } }, - [ - { - method: 'GET', - url: '_stats?level=shards', - data: null, - }, - { - method: 'PUT', - url: 'index_1/type1/1', - data: { - f: 1, - }, - }, - ] - ); - - multiReqTest( - 'before start to after end, with comments', - editorInput1, - { start: { lineNumber: 5 }, end: { lineNumber: 15 } }, - [ - { - method: 'GET', - url: '_stats?level=shards', - data: null, - }, - { - method: 'PUT', - url: 'index_1/type1/1', - data: { - f: 1, - }, - }, - ] - ); - - multiReqTest( - 'between requests', - editorInput1, - { start: { lineNumber: 22 }, end: { lineNumber: 23 } }, - [] - ); - - multiReqTest( - 'between requests - with comment', - editorInput1, - { start: { lineNumber: 21 }, end: { lineNumber: 23 } }, - [] - ); - - multiReqTest( - 'between requests - before comment', - editorInput1, - { start: { lineNumber: 20 }, end: { lineNumber: 23 } }, - [] - ); - - function multiReqCopyAsCurlTest(name, editorInput, range, expected) { - utilsTest('multi request copy as curl - ' + name, editorInput, async function (done) { - const curl = await input.getRequestsAsCURL('http://localhost:9200', range); - expect(curl).toEqual(expected); - done(); - }); - } - - multiReqCopyAsCurlTest( - 'start to end, with comment', - editorInput1, - { start: { lineNumber: 7 }, end: { lineNumber: 14 } }, - ` -curl -XGET "http://localhost:9200/_stats?level=shards" -H "kbn-xsrf: reporting" - -#in between comment - -curl -XPUT "http://localhost:9200/index_1/type1/1" -H "kbn-xsrf: reporting" -H "Content-Type: application/json" -d' -{ - "f": 1 -}'`.trim() - ); - - multiReqCopyAsCurlTest( - 'with single quotes', - editorInput1, - { start: { lineNumber: 29 }, end: { lineNumber: 33 } }, - ` -curl -XPOST "http://localhost:9200/_sql?format=txt" -H "kbn-xsrf: reporting" -H "Content-Type: application/json" -d' -{ - "query": "SELECT prenom FROM claude_index WHERE prenom = '\\''claude'\\'' ", - "fetch_size": 1 -}'`.trim() - ); - - multiReqCopyAsCurlTest( - 'with date math index', - editorInput1, - { start: { lineNumber: 35 }, end: { lineNumber: 35 } }, - ` - curl -XGET "http://localhost:9200/%3Cindex_1-%7Bnow%2Fd-2d%7D%3E%2C%3Cindex_1-%7Bnow%2Fd-1d%7D%3E%2C%3Cindex_1-%7Bnow%2Fd%7D%3E%2F_search?pretty" -H "kbn-xsrf: reporting"`.trim() - ); - - multiReqCopyAsCurlTest( - 'with Kibana API request', - editorInput1, - { start: { lineNumber: 37 }, end: { lineNumber: 37 } }, - ` -curl -XGET "http://localhost:5620/api/spaces/space" -H \"kbn-xsrf: reporting\"`.trim() - ); - - describe('getRequestsAsCURL', () => { - it('should return empty string if no requests', async () => { - input?.getCoreEditor().setValue('', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 1 }, - }); - expect(curl).toEqual(''); - }); - - it('should replace variables in the URL', async () => { - storage.set('variables', [{ name: 'exampleVariableA', value: 'valueA' }]); - input?.getCoreEditor().setValue('GET ${exampleVariableA}', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 1 }, - }); - expect(curl).toContain('valueA'); - }); - - it('should replace variables in the body', async () => { - storage.set('variables', [{ name: 'exampleVariableB', value: 'valueB' }]); - console.log(storage.get('variables')); - input - ?.getCoreEditor() - .setValue('GET _search\n{\t\t"query": {\n\t\t\t"${exampleVariableB}": ""\n\t}\n}', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 6 }, - }); - expect(curl).toContain('valueB'); - }); - - it('should strip comments in the URL', async () => { - input?.getCoreEditor().setValue('GET _search // comment', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 6 }, - }); - expect(curl).not.toContain('comment'); - }); - - it('should strip comments in the body', async () => { - input - ?.getCoreEditor() - .setValue('{\n\t"query": {\n\t\t"match_all": {} // comment \n\t}\n}', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 8 }, - }); - console.log('curl', curl); - expect(curl).not.toContain('comment'); - }); - - it('should strip multi-line comments in the body', async () => { - input - ?.getCoreEditor() - .setValue('{\n\t"query": {\n\t\t"match_all": {} /* comment */\n\t}\n}', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 8 }, - }); - console.log('curl', curl); - expect(curl).not.toContain('comment'); - }); - - it('should replace multiple variables in the URL', async () => { - storage.set('variables', [ - { name: 'exampleVariableA', value: 'valueA' }, - { name: 'exampleVariableB', value: 'valueB' }, - ]); - input?.getCoreEditor().setValue('GET ${exampleVariableA}/${exampleVariableB}', false); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 1 }, - }); - expect(curl).toContain('valueA'); - expect(curl).toContain('valueB'); - }); - - it('should replace multiple variables in the body', async () => { - storage.set('variables', [ - { name: 'exampleVariableA', value: 'valueA' }, - { name: 'exampleVariableB', value: 'valueB' }, - ]); - input - ?.getCoreEditor() - .setValue( - 'GET _search\n{\t\t"query": {\n\t\t\t"${exampleVariableA}": "${exampleVariableB}"\n\t}\n}', - false - ); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 6 }, - }); - expect(curl).toContain('valueA'); - expect(curl).toContain('valueB'); - }); - - it('should replace variables in bulk request', async () => { - storage.set('variables', [ - { name: 'exampleVariableA', value: 'valueA' }, - { name: 'exampleVariableB', value: 'valueB' }, - ]); - input - ?.getCoreEditor() - .setValue( - 'POST _bulk\n{"index": {"_id": "0"}}\n{"field" : "${exampleVariableA}"}\n{"index": {"_id": "1"}}\n{"field" : "${exampleVariableB}"}\n', - false - ); - const curl = await input.getRequestsAsCURL('http://localhost:9200', { - start: { lineNumber: 1 }, - end: { lineNumber: 4 }, - }); - expect(curl).toContain('valueA'); - expect(curl).toContain('valueB'); - }); - }); -}); diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts deleted file mode 100644 index f0ec279fb4ffe..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.mocks.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -/* eslint no-undef: 0 */ - -import '../legacy_core_editor/legacy_core_editor.test.mocks'; - -import jQuery from 'jquery'; -jest.spyOn(jQuery, 'ajax').mockImplementation( - () => - new Promise(() => { - // never resolve - }) as any -); diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts deleted file mode 100644 index f6b0439cb283e..0000000000000 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import _ from 'lodash'; -import { parse } from 'hjson'; -import { XJson } from '@kbn/es-ui-shared-plugin/public'; - -import RowParser from '../../../lib/row_parser'; -import * as utils from '../../../lib/utils'; -import { constructUrl } from '../../../lib/es/es'; - -import { CoreEditor, Position, Range } from '../../../types'; -import { createTokenIterator } from '../../factories'; -import createAutocompleter from '../../../lib/autocomplete/autocomplete'; -import { getStorage, StorageKeys } from '../../../services'; -import { DEFAULT_VARIABLES } from '../../../../common/constants'; - -const { collapseLiteralStrings } = XJson; - -export class SenseEditor { - currentReqRange: (Range & { markerRef: unknown }) | null; - parser: RowParser; - - private readonly autocomplete: ReturnType; - - constructor(private readonly coreEditor: CoreEditor) { - this.currentReqRange = null; - this.parser = new RowParser(this.coreEditor); - this.autocomplete = createAutocompleter({ - coreEditor, - parser: this.parser, - }); - this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions); - this.coreEditor.on( - 'tokenizerUpdate', - this.highlightCurrentRequestsAndUpdateActionBar.bind(this) - ); - this.coreEditor.on('changeCursor', this.highlightCurrentRequestsAndUpdateActionBar.bind(this)); - this.coreEditor.on('changeScrollTop', this.updateActionsBar.bind(this)); - } - - prevRequestStart = (rowOrPos?: number | Position): Position => { - let curRow: number; - - if (rowOrPos == null) { - curRow = this.coreEditor.getCurrentPosition().lineNumber; - } else if (_.isObject(rowOrPos)) { - curRow = (rowOrPos as Position).lineNumber; - } else { - curRow = rowOrPos as number; - } - - while (curRow > 0 && !this.parser.isStartRequestRow(curRow, this.coreEditor)) curRow--; - - return { - lineNumber: curRow, - column: 1, - }; - }; - - nextRequestStart = (rowOrPos?: number | Position) => { - let curRow: number; - if (rowOrPos == null) { - curRow = this.coreEditor.getCurrentPosition().lineNumber; - } else if (_.isObject(rowOrPos)) { - curRow = (rowOrPos as Position).lineNumber; - } else { - curRow = rowOrPos as number; - } - const maxLines = this.coreEditor.getLineCount(); - for (; curRow < maxLines - 1; curRow++) { - if (this.parser.isStartRequestRow(curRow, this.coreEditor)) { - break; - } - } - return { - row: curRow, - column: 0, - }; - }; - - autoIndent = _.debounce(async () => { - await this.coreEditor.waitForLatestTokens(); - const reqRange = await this.getRequestRange(); - if (!reqRange) { - return; - } - const parsedReq = await this.getRequest(); - - if (!parsedReq) { - return; - } - - if (parsedReq.data.some((doc) => utils.hasComments(doc))) { - /** - * Comments require different approach for indentation and do not have condensed format - * We need to delegate indentation logic to coreEditor since it has access to session and other methods used for formatting and indenting the comments - */ - this.coreEditor.autoIndent(parsedReq.range); - return; - } - - if (parsedReq.data && parsedReq.data.length > 0) { - let indent = parsedReq.data.length === 1; // unindent multi docs by default - let formattedData = utils.formatRequestBodyDoc(parsedReq.data, indent); - if (!formattedData.changed) { - // toggle. - indent = !indent; - formattedData = utils.formatRequestBodyDoc(parsedReq.data, indent); - } - parsedReq.data = formattedData.data; - - this.replaceRequestRange(parsedReq, reqRange); - } - }, 25); - - update = async (data: string, reTokenizeAll = false) => { - return this.coreEditor.setValue(data, reTokenizeAll); - }; - - replaceRequestRange = ( - newRequest: { method: string; url: string; data: string | string[] }, - requestRange: Range - ) => { - const text = utils.textFromRequest(newRequest); - if (requestRange) { - this.coreEditor.replaceRange(requestRange, text); - } else { - // just insert where we are - this.coreEditor.insert(this.coreEditor.getCurrentPosition(), text); - } - }; - - getRequestRange = async (lineNumber?: number): Promise => { - await this.coreEditor.waitForLatestTokens(); - - if (this.parser.isInBetweenRequestsRow(lineNumber)) { - return null; - } - - const reqStart = this.prevRequestStart(lineNumber); - const reqEnd = this.nextRequestEnd(reqStart); - - return { - start: { - ...reqStart, - }, - end: { - ...reqEnd, - }, - }; - }; - - expandRangeToRequestEdges = async ( - range = this.coreEditor.getSelectionRange() - ): Promise => { - await this.coreEditor.waitForLatestTokens(); - - let startLineNumber = range.start.lineNumber; - let endLineNumber = range.end.lineNumber; - const maxLine = Math.max(1, this.coreEditor.getLineCount()); - - if (this.parser.isInBetweenRequestsRow(startLineNumber)) { - /* Do nothing... */ - } else { - for (; startLineNumber >= 1; startLineNumber--) { - if (this.parser.isStartRequestRow(startLineNumber)) { - break; - } - } - } - - if (startLineNumber < 1 || startLineNumber > endLineNumber) { - return null; - } - // move end row to the previous request end if between requests, otherwise walk forward - if (this.parser.isInBetweenRequestsRow(endLineNumber)) { - for (; endLineNumber >= startLineNumber; endLineNumber--) { - if (this.parser.isEndRequestRow(endLineNumber)) { - break; - } - } - } else { - for (; endLineNumber <= maxLine; endLineNumber++) { - if (this.parser.isEndRequestRow(endLineNumber)) { - break; - } - } - } - - if (endLineNumber < startLineNumber || endLineNumber > maxLine) { - return null; - } - - const endColumn = - (this.coreEditor.getLineValue(endLineNumber) || '').replace(/\s+$/, '').length + 1; - return { - start: { - lineNumber: startLineNumber, - column: 1, - }, - end: { - lineNumber: endLineNumber, - column: endColumn, - }, - }; - }; - - getRequestInRange = async (range?: Range) => { - await this.coreEditor.waitForLatestTokens(); - if (!range) { - return null; - } - const request: { - method: string; - data: string[]; - url: string; - range: Range; - } = { - method: '', - data: [], - url: '', - range, - }; - - const pos = range.start; - const tokenIter = createTokenIterator({ editor: this.coreEditor, position: pos }); - let t = tokenIter.getCurrentToken(); - if (this.parser.isEmptyToken(t)) { - // if the row starts with some spaces, skip them. - t = this.parser.nextNonEmptyToken(tokenIter); - } - if (t == null) { - return null; - } - - request.method = t.value; - t = this.parser.nextNonEmptyToken(tokenIter); - - if (!t || t.type === 'method') { - return null; - } - - request.url = ''; - - while (t && t.type && (t.type.indexOf('url') === 0 || t.type === 'variable.template')) { - request.url += t.value; - t = tokenIter.stepForward(); - } - if (this.parser.isEmptyToken(t)) { - // if the url row ends with some spaces, skip them. - t = this.parser.nextNonEmptyToken(tokenIter); - } - - // If the url row ends with a comment, skip it - while (this.parser.isCommentToken(t)) { - t = tokenIter.stepForward(); - } - - let bodyStartLineNumber = (t ? 0 : 1) + tokenIter.getCurrentPosition().lineNumber; // artificially increase end of docs. - let dataEndPos: Position; - while ( - bodyStartLineNumber < range.end.lineNumber || - (bodyStartLineNumber === range.end.lineNumber && 1 < range.end.column) - ) { - dataEndPos = this.nextDataDocEnd({ - lineNumber: bodyStartLineNumber, - column: 1, - }); - const bodyRange: Range = { - start: { - lineNumber: bodyStartLineNumber, - column: 1, - }, - end: dataEndPos, - }; - const data = this.coreEditor.getValueInRange(bodyRange)!; - request.data.push(data.trim()); - bodyStartLineNumber = dataEndPos.lineNumber + 1; - } - - return request; - }; - - getRequestsInRange = async ( - range = this.coreEditor.getSelectionRange(), - includeNonRequestBlocks = false - ): Promise => { - await this.coreEditor.waitForLatestTokens(); - if (!range) { - return []; - } - - const expandedRange = await this.expandRangeToRequestEdges(range); - if (!expandedRange) { - return []; - } - - const requests: unknown[] = []; - - let rangeStartCursor = expandedRange.start.lineNumber; - const endLineNumber = expandedRange.end.lineNumber; - - // move to the next request start (during the second iterations this may not be exactly on a request - let currentLineNumber = expandedRange.start.lineNumber; - - const flushNonRequestBlock = () => { - if (includeNonRequestBlocks) { - const nonRequestPrefixBlock = this.coreEditor - .getLines(rangeStartCursor, currentLineNumber - 1) - .join('\n'); - if (nonRequestPrefixBlock) { - requests.push(nonRequestPrefixBlock); - } - } - }; - - while (currentLineNumber <= endLineNumber) { - if (this.parser.isStartRequestRow(currentLineNumber)) { - flushNonRequestBlock(); - const request = await this.getRequest(currentLineNumber); - if (!request) { - // Something has probably gone wrong. - return requests; - } else { - requests.push(request); - rangeStartCursor = currentLineNumber = request.range.end.lineNumber + 1; - } - } else { - ++currentLineNumber; - } - } - - flushNonRequestBlock(); - - return requests; - }; - - getRequest = async (row?: number) => { - await this.coreEditor.waitForLatestTokens(); - if (this.parser.isInBetweenRequestsRow(row)) { - return null; - } - - const range = await this.getRequestRange(row); - return this.getRequestInRange(range!); - }; - - moveToPreviousRequestEdge = async () => { - await this.coreEditor.waitForLatestTokens(); - const pos = this.coreEditor.getCurrentPosition(); - for ( - pos.lineNumber--; - pos.lineNumber > 1 && !this.parser.isRequestEdge(pos.lineNumber); - pos.lineNumber-- - ) { - // loop for side effects - } - this.coreEditor.moveCursorToPosition({ - lineNumber: pos.lineNumber, - column: 1, - }); - }; - - moveToNextRequestEdge = async (moveOnlyIfNotOnEdge: boolean) => { - await this.coreEditor.waitForLatestTokens(); - const pos = this.coreEditor.getCurrentPosition(); - const maxRow = this.coreEditor.getLineCount(); - if (!moveOnlyIfNotOnEdge) { - pos.lineNumber++; - } - for ( - ; - pos.lineNumber < maxRow && !this.parser.isRequestEdge(pos.lineNumber); - pos.lineNumber++ - ) { - // loop for side effects - } - this.coreEditor.moveCursorToPosition({ - lineNumber: pos.lineNumber, - column: 1, - }); - }; - - nextRequestEnd = (pos: Position): Position => { - pos = pos || this.coreEditor.getCurrentPosition(); - const maxLines = this.coreEditor.getLineCount(); - let curLineNumber = pos.lineNumber; - for (; curLineNumber <= maxLines; ++curLineNumber) { - const curRowMode = this.parser.getRowParseMode(curLineNumber); - // eslint-disable-next-line no-bitwise - if ((curRowMode & this.parser.MODE.REQUEST_END) > 0) { - break; - } - // eslint-disable-next-line no-bitwise - if (curLineNumber !== pos.lineNumber && (curRowMode & this.parser.MODE.REQUEST_START) > 0) { - break; - } - } - - const column = - (this.coreEditor.getLineValue(curLineNumber) || '').replace(/\s+$/, '').length + 1; - - return { - lineNumber: curLineNumber, - column, - }; - }; - - nextDataDocEnd = (pos: Position): Position => { - pos = pos || this.coreEditor.getCurrentPosition(); - let curLineNumber = pos.lineNumber; - const maxLines = this.coreEditor.getLineCount(); - for (; curLineNumber < maxLines; curLineNumber++) { - const curRowMode = this.parser.getRowParseMode(curLineNumber); - // eslint-disable-next-line no-bitwise - if ((curRowMode & this.parser.MODE.REQUEST_END) > 0) { - break; - } - // eslint-disable-next-line no-bitwise - if ((curRowMode & this.parser.MODE.MULTI_DOC_CUR_DOC_END) > 0) { - break; - } - // eslint-disable-next-line no-bitwise - if (curLineNumber !== pos.lineNumber && (curRowMode & this.parser.MODE.REQUEST_START) > 0) { - break; - } - } - - const column = - (this.coreEditor.getLineValue(curLineNumber) || '').length + - 1; /* Range goes to 1 after last char */ - - return { - lineNumber: curLineNumber, - column, - }; - }; - - highlightCurrentRequestsAndUpdateActionBar = _.debounce(async () => { - await this.coreEditor.waitForLatestTokens(); - const expandedRange = await this.expandRangeToRequestEdges(); - if (expandedRange === null && this.currentReqRange === null) { - return; - } - if ( - expandedRange !== null && - this.currentReqRange !== null && - expandedRange.start.lineNumber === this.currentReqRange.start.lineNumber && - expandedRange.end.lineNumber === this.currentReqRange.end.lineNumber - ) { - // same request, now see if we are on the first line and update the action bar - const cursorLineNumber = this.coreEditor.getCurrentPosition().lineNumber; - if (cursorLineNumber === this.currentReqRange.start.lineNumber) { - this.updateActionsBar(); - } - return; // nothing to do.. - } - - if (this.currentReqRange) { - this.coreEditor.removeMarker(this.currentReqRange.markerRef); - } - - this.currentReqRange = expandedRange as any; - if (this.currentReqRange) { - this.currentReqRange.markerRef = this.coreEditor.addMarker(this.currentReqRange); - } - this.updateActionsBar(); - }, 25); - - getRequestsAsCURL = async (elasticsearchBaseUrl: string, range?: Range): Promise => { - const variables = getStorage().get(StorageKeys.VARIABLES, DEFAULT_VARIABLES); - let requests = await this.getRequestsInRange(range, true); - requests = utils.replaceVariables(requests, variables); - const result = _.map(requests, (req) => { - if (typeof req === 'string') { - // no request block - return req; - } - - const path = req.url; - const method = req.method; - const data = req.data; - - // this is the first url defined in elasticsearch.hosts - const url = constructUrl(elasticsearchBaseUrl, path); - - // Append 'kbn-xsrf' header to bypass (XSRF/CSRF) protections - let ret = `curl -X${method.toUpperCase()} "${url}" -H "kbn-xsrf: reporting"`; - - if (data && data.length) { - const joinedData = data.join('\n'); - let dataAsString: string; - - try { - ret += ` -H "Content-Type: application/json" -d'\n`; - - if (utils.hasComments(joinedData)) { - // if there are comments in the data, we need to strip them out - const dataWithoutComments = parse(joinedData); - dataAsString = collapseLiteralStrings(JSON.stringify(dataWithoutComments, null, 2)); - } else { - dataAsString = collapseLiteralStrings(joinedData); - } - // We escape single quoted strings that are wrapped in single quoted strings - ret += dataAsString.replace(/'/g, "'\\''"); - if (data.length > 1) { - ret += '\n'; - } // end with a new line - ret += "'"; - } catch (e) { - throw new Error(`Error parsing data: ${e.message}`); - } - } - return ret; - }); - - return result.join('\n'); - }; - - updateActionsBar = () => { - return this.coreEditor.legacyUpdateUI(this.currentReqRange); - }; - - getCoreEditor() { - return this.coreEditor; - } -} diff --git a/src/plugins/console/public/application/stores/editor.ts b/src/plugins/console/public/application/stores/editor.ts index 556f4f64337e6..8ae24e5a422b7 100644 --- a/src/plugins/console/public/application/stores/editor.ts +++ b/src/plugins/console/public/application/stores/editor.ts @@ -12,7 +12,6 @@ import { produce } from 'immer'; import { identity } from 'fp-ts/lib/function'; import { DevToolsSettings, DEFAULT_SETTINGS } from '../../services'; import { TextObject } from '../../../common/text_object'; -import { SenseEditor } from '../models'; import { SHELL_TAB_ID } from '../containers/main/constants'; import { MonacoEditorActionsProvider } from '../containers/editor/monaco_editor_actions_provider'; import { RequestToRestore } from '../../types'; @@ -39,7 +38,7 @@ export const initialValue: Store = produce( ); export type Action = - | { type: 'setInputEditor'; payload: SenseEditor | MonacoEditorActionsProvider } + | { type: 'setInputEditor'; payload: MonacoEditorActionsProvider } | { type: 'setCurrentTextObject'; payload: TextObject } | { type: 'updateSettings'; payload: DevToolsSettings } | { type: 'setCurrentView'; payload: string } diff --git a/src/plugins/console/public/lib/ace_token_provider/index.ts b/src/plugins/console/public/lib/ace_token_provider/index.ts deleted file mode 100644 index 8819ac19a1262..0000000000000 --- a/src/plugins/console/public/lib/ace_token_provider/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export * from './token_provider'; diff --git a/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts b/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts deleted file mode 100644 index b36d9855414bd..0000000000000 --- a/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import '../../application/models/sense_editor/sense_editor.test.mocks'; - -import $ from 'jquery'; - -// TODO: -// We import from application models as a convenient way to bootstrap loading up of an editor using -// this lib. We also need to import application specific mocks which is not ideal. -// In this situation, the token provider lib knows about app models in tests, which it really shouldn't. Should create -// a better sandbox in future. -import { create, SenseEditor } from '../../application/models/sense_editor'; - -import { Position, Token, TokensProvider } from '../../types'; - -interface RunTestArgs { - input: string; - done?: () => void; -} - -describe('Ace (legacy) token provider', () => { - let senseEditor: SenseEditor; - let tokenProvider: TokensProvider; - beforeEach(() => { - // Set up our document body - document.body.innerHTML = `
-
-
-
-
`; - - senseEditor = create(document.querySelector('#ConAppEditor')!); - - $(senseEditor.getCoreEditor().getContainer())!.show(); - - (senseEditor as any).autocomplete._test.removeChangeListener(); - tokenProvider = senseEditor.getCoreEditor().getTokenProvider(); - }); - - afterEach(async () => { - $(senseEditor.getCoreEditor().getContainer())!.hide(); - (senseEditor as any).autocomplete._test.addChangeListener(); - await senseEditor.update('', true); - }); - - describe('#getTokens', () => { - const runTest = ({ - input, - expectedTokens, - done, - lineNumber = 1, - }: RunTestArgs & { expectedTokens: Token[] | null; lineNumber?: number }) => { - senseEditor.update(input, true).then(() => { - const tokens = tokenProvider.getTokens(lineNumber); - expect(tokens).toEqual(expectedTokens); - if (done) done(); - }); - }; - - describe('base cases', () => { - test('case 1 - only url', (done) => { - runTest({ - input: `GET http://somehost/_search`, - expectedTokens: [ - { type: 'method', value: 'GET', position: { lineNumber: 1, column: 1 } }, - { type: 'whitespace', value: ' ', position: { lineNumber: 1, column: 4 } }, - { - type: 'url.protocol_host', - value: 'http://somehost', - position: { lineNumber: 1, column: 5 }, - }, - { type: 'url.slash', value: '/', position: { lineNumber: 1, column: 20 } }, - { type: 'url.part', value: '_search', position: { lineNumber: 1, column: 21 } }, - ], - done, - }); - }); - - test('case 2 - basic auth in host name', (done) => { - runTest({ - input: `GET http://test:user@somehost/`, - expectedTokens: [ - { type: 'method', value: 'GET', position: { lineNumber: 1, column: 1 } }, - { type: 'whitespace', value: ' ', position: { lineNumber: 1, column: 4 } }, - { - type: 'url.protocol_host', - value: 'http://test:user@somehost', - position: { lineNumber: 1, column: 5 }, - }, - { type: 'url.slash', value: '/', position: { lineNumber: 1, column: 30 } }, - ], - done, - }); - }); - - test('case 3 - handles empty lines', (done) => { - runTest({ - input: `POST abc - - -{ -`, - expectedTokens: [ - { type: 'method', value: 'POST', position: { lineNumber: 1, column: 1 } }, - { type: 'whitespace', value: ' ', position: { lineNumber: 1, column: 5 } }, - { type: 'url.part', value: 'abc', position: { lineNumber: 1, column: 6 } }, - ], - done, - lineNumber: 1, - }); - }); - }); - - describe('with newlines', () => { - test('case 1 - newlines base case', (done) => { - runTest({ - input: `GET http://test:user@somehost/ -{ - "wudup": "!" -}`, - expectedTokens: [ - { type: 'whitespace', value: ' ', position: { lineNumber: 3, column: 1 } }, - { type: 'variable', value: '"wudup"', position: { lineNumber: 3, column: 3 } }, - { type: 'punctuation.colon', value: ':', position: { lineNumber: 3, column: 10 } }, - { type: 'whitespace', value: ' ', position: { lineNumber: 3, column: 11 } }, - { type: 'string', value: '"!"', position: { lineNumber: 3, column: 12 } }, - ], - done, - lineNumber: 3, - }); - }); - }); - - describe('edge cases', () => { - test('case 1 - getting token outside of document', (done) => { - runTest({ - input: `GET http://test:user@somehost/ -{ - "wudup": "!" -}`, - expectedTokens: null, - done, - lineNumber: 100, - }); - }); - - test('case 2 - empty lines', (done) => { - runTest({ - input: `GET http://test:user@somehost/ - - - - -{ - "wudup": "!" -}`, - expectedTokens: [], - done, - lineNumber: 5, - }); - }); - }); - }); - - describe('#getTokenAt', () => { - const runTest = ({ - input, - expectedToken, - done, - position, - }: RunTestArgs & { expectedToken: Token | null; position: Position }) => { - senseEditor.update(input, true).then(() => { - const tokens = tokenProvider.getTokenAt(position); - expect(tokens).toEqual(expectedToken); - if (done) done(); - }); - }; - - describe('base cases', () => { - it('case 1 - gets a token from the url', (done) => { - const input = `GET http://test:user@somehost/`; - runTest({ - input, - expectedToken: { - position: { lineNumber: 1, column: 4 }, - type: 'whitespace', - value: ' ', - }, - position: { lineNumber: 1, column: 5 }, - }); - - runTest({ - input, - expectedToken: { - position: { lineNumber: 1, column: 5 }, - type: 'url.protocol_host', - value: 'http://test:user@somehost', - }, - position: { lineNumber: 1, column: input.length }, - done, - }); - }); - }); - - describe('special cases', () => { - it('case 1 - handles input outside of range', (done) => { - runTest({ - input: `GET abc`, - expectedToken: null, - done, - position: { lineNumber: 1, column: 99 }, - }); - }); - }); - }); -}); diff --git a/src/plugins/console/public/lib/ace_token_provider/token_provider.ts b/src/plugins/console/public/lib/ace_token_provider/token_provider.ts deleted file mode 100644 index 9e61771946771..0000000000000 --- a/src/plugins/console/public/lib/ace_token_provider/token_provider.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { IEditSession, TokenInfo as BraceTokenInfo } from 'brace'; -import { TokensProvider, Token, Position } from '../../types'; - -// Brace's token information types are not accurate. -interface TokenInfo extends BraceTokenInfo { - type: string; -} - -const toToken = (lineNumber: number, column: number, token: TokenInfo): Token => ({ - type: token.type, - value: token.value, - position: { - lineNumber, - column, - }, -}); - -const toTokens = (lineNumber: number, tokens: TokenInfo[]): Token[] => { - let acc = ''; - return tokens.map((token) => { - const column = acc.length + 1; - acc += token.value; - return toToken(lineNumber, column, token); - }); -}; - -const extractTokenFromAceTokenRow = ( - lineNumber: number, - column: number, - aceTokens: TokenInfo[] -) => { - let acc = ''; - for (const token of aceTokens) { - const start = acc.length + 1; - acc += token.value; - const end = acc.length; - if (column < start) continue; - if (column > end + 1) continue; - return toToken(lineNumber, start, token); - } - return null; -}; - -export class AceTokensProvider implements TokensProvider { - constructor(private readonly session: IEditSession) {} - - getTokens(lineNumber: number): Token[] | null { - if (lineNumber < 1) return null; - - // Important: must use a .session.getLength because this is a cached value. - // Calculating line length here will lead to performance issues because this function - // may be called inside of tight loops. - const lineCount = this.session.getLength(); - if (lineNumber > lineCount) { - return null; - } - - const tokens = this.session.getTokens(lineNumber - 1) as unknown as TokenInfo[]; - if (!tokens || !tokens.length) { - // We are inside of the document but have no tokens for this line. Return an empty - // array to represent this empty line. - return []; - } - - return toTokens(lineNumber, tokens); - } - - getTokenAt(pos: Position): Token | null { - const tokens = this.session.getTokens(pos.lineNumber - 1) as unknown as TokenInfo[]; - if (tokens) { - return extractTokenFromAceTokenRow(pos.lineNumber, pos.column, tokens); - } - return null; - } -} diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts deleted file mode 100644 index 73ef1981cfc0b..0000000000000 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ /dev/null @@ -1,1316 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; - -// TODO: All of these imports need to be moved to the core editor so that it can inject components from there. -import { - getEndpointBodyCompleteComponents, - getGlobalAutocompleteComponents, - getTopLevelUrlCompleteComponents, - getUnmatchedEndpointComponents, -} from '../kb/kb'; - -import { createTokenIterator } from '../../application/factories'; -import type { CoreEditor, Position, Range, Token } from '../../types'; -import type RowParser from '../row_parser'; - -import * as utils from '../utils'; - -import { populateContext } from './engine'; -import type { AutoCompleteContext, DataAutoCompleteRulesOneOf, ResultTerm } from './types'; -import { URL_PATH_END_MARKER, ConstantComponent } from './components'; -import { looksLikeTypingIn } from './looks_like_typing_in'; - -let lastEvaluatedToken: Token | null = null; - -function isUrlParamsToken(token: { type: string } | null) { - switch ((token || {}).type) { - case 'url.param': - case 'url.equal': - case 'url.value': - case 'url.questionmark': - case 'url.amp': - return true; - default: - return false; - } -} - -/* Logs the provided arguments to the console if the `window.autocomplete_trace` flag is set to true. - * This function checks if the `autocomplete_trace` flag is enabled on the `window` object. This is - * only used when executing functional tests. - * If the flag is enabled, it logs each argument to the console. - * If an argument is an object, it is stringified before logging. - */ -const tracer = (...args: any[]) => { - // @ts-ignore - if (window.autocomplete_trace) { - // eslint-disable-next-line no-console - console.log.call( - console, - ..._.map(args, (arg) => { - return typeof arg === 'object' ? JSON.stringify(arg) : arg; - }) - ); - } -}; - -/** - * Get the method and token paths for a specific position in the current editor buffer. - * - * This function can be used for getting autocomplete information or for getting more information - * about the endpoint associated with autocomplete. In future, these concerns should be better - * separated. - * - */ -export function getCurrentMethodAndTokenPaths( - editor: CoreEditor, - pos: Position, - parser: RowParser, - forceEndOfUrl?: boolean /* Flag for indicating whether we want to avoid early escape optimization. */ -) { - const tokenIter = createTokenIterator({ - editor, - position: pos, - }); - const startPos = pos; - let bodyTokenPath: string[] | null = []; - const ret: AutoCompleteContext = {}; - - const STATES = { - looking_for_key: 0, // looking for a key but without jumping over anything but white space and colon. - looking_for_scope_start: 1, // skip everything until scope start - start: 3, - }; - let state = STATES.start; - - // initialization problems - - let t = tokenIter.getCurrentToken(); - if (t) { - if (startPos.column === 1) { - // if we are at the beginning of the line, the current token is the one after cursor, not before which - // deviates from the standard. - t = tokenIter.stepBackward(); - state = STATES.looking_for_scope_start; - } - } else { - if (startPos.column === 1) { - // empty lines do no have tokens, move one back - t = tokenIter.stepBackward(); - state = STATES.start; - } - } - - let walkedSomeBody = false; - - // climb one scope at a time and get the scope key - for (; t && t.type.indexOf('url') === -1 && t.type !== 'method'; t = tokenIter.stepBackward()) { - if (t.type !== 'whitespace') { - walkedSomeBody = true; - } // marks we saw something - - switch (t.type) { - case 'variable': - if (state === STATES.looking_for_key) { - bodyTokenPath.unshift(t.value.trim().replace(/"/g, '')); - } - state = STATES.looking_for_scope_start; // skip everything until the beginning of this scope - break; - - case 'paren.lparen': - bodyTokenPath.unshift(t.value); - if (state === STATES.looking_for_scope_start) { - // found it. go look for the relevant key - state = STATES.looking_for_key; - } - break; - case 'paren.rparen': - // reset he search for key - state = STATES.looking_for_scope_start; - // and ignore this sub scope.. - let parenCount = 1; - t = tokenIter.stepBackward(); - while (t && parenCount > 0) { - switch (t.type) { - case 'paren.lparen': - parenCount--; - break; - case 'paren.rparen': - parenCount++; - break; - } - if (parenCount > 0) { - t = tokenIter.stepBackward(); - } - } - if (!t) { - tracer(`paren.rparen: oops we run out.. we don't know what's up return null`); - return {}; - } - continue; - case 'punctuation.end_triple_quote': - // reset the search for key - state = STATES.looking_for_scope_start; - for (t = tokenIter.stepBackward(); t; t = tokenIter.stepBackward()) { - if (t.type === 'punctuation.start_triple_quote') { - t = tokenIter.stepBackward(); - break; - } - } - if (!t) { - tracer(`paren.rparen: oops we run out.. we don't know what's up return null`); - return {}; - } - continue; - case 'punctuation.start_triple_quote': - if (state === STATES.start) { - state = STATES.looking_for_key; - } else if (state === STATES.looking_for_key) { - state = STATES.looking_for_scope_start; - } - bodyTokenPath.unshift('"""'); - continue; - case 'string': - case 'constant.numeric': - case 'constant.language.boolean': - case 'text': - if (state === STATES.start) { - state = STATES.looking_for_key; - } else if (state === STATES.looking_for_key) { - state = STATES.looking_for_scope_start; - } - - break; - case 'punctuation.comma': - if (state === STATES.start) { - state = STATES.looking_for_scope_start; - } - break; - case 'punctuation.colon': - case 'whitespace': - if (state === STATES.start) { - state = STATES.looking_for_key; - } - break; // skip white space - } - } - - if (walkedSomeBody && (!bodyTokenPath || bodyTokenPath.length === 0) && !forceEndOfUrl) { - tracer( - 'we had some content and still no path', - '-> the cursor is position after a closed body', - '-> no auto complete' - ); - return {}; - } - - ret.urlTokenPath = []; - if (tokenIter.getCurrentPosition().lineNumber === startPos.lineNumber) { - if (t && (t.type === 'url.part' || t.type === 'url.param' || t.type === 'url.value')) { - // we are forcing the end of the url for the purposes of determining an endpoint - if (forceEndOfUrl && t.type === 'url.part') { - ret.urlTokenPath.push(t.value); - ret.urlTokenPath.push(URL_PATH_END_MARKER); - } - // we are on the same line as cursor and dealing with a url. Current token is not part of the context - t = tokenIter.stepBackward(); - // This will force method parsing - while (t!.type === 'whitespace') { - t = tokenIter.stepBackward(); - } - } - bodyTokenPath = null; // no not on a body line. - } - - ret.bodyTokenPath = bodyTokenPath; - - ret.urlParamsTokenPath = null; - ret.requestStartRow = tokenIter.getCurrentPosition().lineNumber; - let curUrlPart: - | null - | string - | Array> - | undefined - | Record; - - while (t && isUrlParamsToken(t)) { - switch (t.type) { - case 'url.value': - if (Array.isArray(curUrlPart)) { - curUrlPart.unshift(t.value); - } else if (curUrlPart) { - curUrlPart = [t.value, curUrlPart]; - } else { - curUrlPart = t.value; - } - break; - case 'url.comma': - if (!curUrlPart) { - curUrlPart = []; - } else if (!Array.isArray(curUrlPart)) { - curUrlPart = [curUrlPart]; - } - break; - case 'url.param': - const v = curUrlPart; - curUrlPart = {}; - curUrlPart[t.value] = v; - break; - case 'url.amp': - case 'url.questionmark': - if (!ret.urlParamsTokenPath) { - ret.urlParamsTokenPath = []; - } - ret.urlParamsTokenPath.unshift((curUrlPart as Record) || {}); - curUrlPart = null; - break; - } - t = tokenIter.stepBackward(); - } - - curUrlPart = null; - while (t && t.type.indexOf('url') !== -1) { - switch (t.type) { - case 'url.part': - if (Array.isArray(curUrlPart)) { - curUrlPart.unshift(t.value); - } else if (curUrlPart) { - curUrlPart = [t.value, curUrlPart]; - } else { - curUrlPart = t.value; - } - break; - case 'url.comma': - if (!curUrlPart) { - curUrlPart = []; - } else if (!Array.isArray(curUrlPart)) { - curUrlPart = [curUrlPart]; - } - break; - case 'url.slash': - if (curUrlPart) { - ret.urlTokenPath.unshift(curUrlPart as string); - curUrlPart = null; - } - break; - } - t = parser.prevNonEmptyToken(tokenIter); - } - - if (curUrlPart) { - ret.urlTokenPath.unshift(curUrlPart as string); - } - - if (!ret.bodyTokenPath && !ret.urlParamsTokenPath) { - if (ret.urlTokenPath.length > 0) { - // // started on the url, first token is current token - ret.otherTokenValues = ret.urlTokenPath[0]; - } - } else { - // mark the url as completed. - ret.urlTokenPath.push(URL_PATH_END_MARKER); - } - - if (t && t.type === 'method') { - ret.method = t.value; - } - return ret; -} - -// eslint-disable-next-line import/no-default-export -export default function ({ - coreEditor: editor, - parser, -}: { - coreEditor: CoreEditor; - parser: RowParser; -}) { - function isUrlPathToken(token: Token | null) { - switch ((token || ({} as Token)).type) { - case 'url.slash': - case 'url.comma': - case 'url.part': - return true; - default: - return false; - } - } - - function addMetaToTermsList(list: ResultTerm[], meta: string, template?: string): ResultTerm[] { - return _.map(list, function (t) { - if (typeof t !== 'object') { - t = { name: t }; - } - return _.defaults(t, { meta, template }); - }); - } - - function replaceLinesWithPrefixPieces(prefixPieces: string[], startLineNumber: number) { - const middlePiecesCount = prefixPieces.length - 1; - prefixPieces.forEach((piece, index) => { - if (index >= middlePiecesCount) { - return; - } - const line = startLineNumber + index + 1; - const column = editor.getLineValue(line).length - 1; - const start = { lineNumber: line, column: 0 }; - const end = { lineNumber: line, column }; - editor.replace({ start, end }, piece); - }); - } - - /** - * Get a different set of templates based on the value configured in the request. - * For example, when creating a snapshot repository of different types (`fs`, `url` etc), - * different properties are inserted in the textarea based on the type. - * E.g. https://github.com/elastic/kibana/blob/main/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create_repository.json - */ - function getConditionalTemplate( - name: string, - autocompleteRules: Record | null | undefined - ) { - const obj = autocompleteRules && autocompleteRules[name]; - - if (obj) { - const currentLineNumber = editor.getCurrentPosition().lineNumber; - - if (hasOneOfIn(obj)) { - // Get the line number of value that should provide different templates based on that - const startLine = getStartLineNumber(currentLineNumber, obj.__one_of); - // Join line values from start to current line - const lines = editor.getLines(startLine, currentLineNumber).join('\n'); - // Get the correct template by comparing the autocomplete rules against the lines - const prop = getProperty(lines, obj.__one_of); - if (prop && prop.__template) { - return prop.__template; - } - } - } - } - - /** - * Check if object has a property of '__one_of' - */ - function hasOneOfIn(value: unknown): value is { __one_of: DataAutoCompleteRulesOneOf[] } { - return typeof value === 'object' && value !== null && '__one_of' in value; - } - - /** - * Get the start line of value that matches the autocomplete rules condition - */ - function getStartLineNumber(currentLine: number, rules: DataAutoCompleteRulesOneOf[]): number { - if (currentLine === 1) { - return currentLine; - } - const value = editor.getLineValue(currentLine); - const prop = getProperty(value, rules); - if (prop) { - return currentLine; - } - return getStartLineNumber(currentLine - 1, rules); - } - - /** - * Get the matching property based on the given condition - */ - function getProperty(condition: string, rules: DataAutoCompleteRulesOneOf[]) { - return rules.find((rule) => { - if (rule.__condition && rule.__condition.lines_regex) { - return new RegExp(rule.__condition.lines_regex, 'm').test(condition); - } - return false; - }); - } - - function applyTerm(term: ResultTerm) { - const context = term.context!; - - if (context?.endpoint && term.value) { - const { data_autocomplete_rules: autocompleteRules } = context.endpoint; - const template = getConditionalTemplate(term.value, autocompleteRules); - if (template) { - term.template = template; - } - } - // make sure we get up to date replacement info. - addReplacementInfoToContext(context, editor.getCurrentPosition(), term.insertValue); - - let termAsString; - if (context.autoCompleteType === 'body') { - termAsString = - typeof term.insertValue === 'string' ? '"' + term.insertValue + '"' : term.insertValue + ''; - if (term.insertValue === '[' || term.insertValue === '{') { - termAsString = ''; - } - } else { - termAsString = term.insertValue + ''; - } - - let valueToInsert = termAsString; - let templateInserted = false; - if (context.addTemplate && !_.isUndefined(term.template) && !_.isNull(term.template)) { - let indentedTemplateLines; - // In order to allow triple quoted strings in template completion we check the `__raw_` - // attribute to determine whether this template should go through JSON formatting. - if (term.template.__raw && term.template.value) { - indentedTemplateLines = term.template.value.split('\n'); - } else { - indentedTemplateLines = utils.jsonToString(term.template, true).split('\n'); - } - let currentIndentation = editor.getLineValue(context.rangeToReplace!.start.lineNumber); - currentIndentation = currentIndentation.match(/^\s*/)![0]; - for ( - let i = 1; - i < indentedTemplateLines.length; - i++ // skip first line - ) { - indentedTemplateLines[i] = currentIndentation + indentedTemplateLines[i]; - } - - valueToInsert += ': ' + indentedTemplateLines.join('\n'); - templateInserted = true; - } else { - templateInserted = true; - if (term.value === '[') { - valueToInsert += '[]'; - } else if (term.value === '{') { - valueToInsert += '{}'; - } else { - templateInserted = false; - } - } - const linesToMoveDown = (context.prefixToAdd ?? '').match(/\n|\r/g)?.length ?? 0; - - let prefix = context.prefixToAdd ?? ''; - - // disable listening to the changes we are making. - editor.off('changeSelection', editorChangeListener); - - // if should add chars on the previous not empty line - if (linesToMoveDown) { - const [firstPart = '', ...prefixPieces] = context.prefixToAdd?.split(/\n|\r/g) ?? []; - const lastPart = _.last(prefixPieces) ?? ''; - const { start } = context.rangeToReplace!; - const end = { ...start, column: start.column + firstPart.length }; - - // adding only the content of prefix before newlines - editor.replace({ start, end }, firstPart); - - // replacing prefix pieces without the last one, which is handled separately - if (prefixPieces.length - 1 > 0) { - replaceLinesWithPrefixPieces(prefixPieces, start.lineNumber); - } - - // and the last prefix line, keeping the editor's own newlines. - prefix = lastPart; - context.rangeToReplace!.start.lineNumber = context.rangeToReplace!.end.lineNumber; - context.rangeToReplace!.start.column = 0; - } - - valueToInsert = prefix + valueToInsert + context.suffixToAdd; - - if (context.rangeToReplace!.start.column !== context.rangeToReplace!.end.column) { - editor.replace(context.rangeToReplace!, valueToInsert); - } else { - editor.insert(valueToInsert); - } - - editor.clearSelection(); // for some reason the above changes selection - - // go back to see whether we have one of ( : { & [ do not require a comma. All the rest do. - let newPos = { - lineNumber: context.rangeToReplace!.start.lineNumber, - column: - context.rangeToReplace!.start.column + - termAsString.length + - prefix.length + - (templateInserted ? 0 : context.suffixToAdd!.length), - }; - - const tokenIter = createTokenIterator({ - editor, - position: newPos, - }); - - if (context.autoCompleteType === 'body') { - // look for the next place stand, just after a comma, { - let nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - switch (nonEmptyToken ? nonEmptyToken.type : 'NOTOKEN') { - case 'paren.rparen': - newPos = tokenIter.getCurrentPosition(); - break; - case 'punctuation.colon': - nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - if ((nonEmptyToken || ({} as Token)).type === 'paren.lparen') { - nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - newPos = tokenIter.getCurrentPosition(); - if (nonEmptyToken && nonEmptyToken.value.indexOf('"') === 0) { - newPos.column++; - } // don't stand on " - } - break; - case 'paren.lparen': - case 'punctuation.comma': - tokenIter.stepForward(); - newPos = tokenIter.getCurrentPosition(); - break; - } - editor.moveCursorToPosition(newPos); - } - - // re-enable listening to typing - editor.on('changeSelection', editorChangeListener); - } - - function getAutoCompleteContext(ctxEditor: CoreEditor, pos: Position) { - // deduces all the parameters need to position and insert the auto complete - const context: AutoCompleteContext = { - autoCompleteSet: null, // instructions for what can be here - endpoint: null, - urlPath: null, - method: null, - activeScheme: null, - editor: ctxEditor, - }; - - // context.updatedForToken = session.getTokenAt(pos.row, pos.column); - // - // if (!context.updatedForToken) - // context.updatedForToken = { value: "", start: pos.column }; // empty line - // - // context.updatedForToken.row = pos.row; // extend - - context.autoCompleteType = getAutoCompleteType(pos); - switch (context.autoCompleteType) { - case 'path': - addPathAutoCompleteSetToContext(context, pos); - break; - case 'url_params': - addUrlParamsAutoCompleteSetToContext(context, pos); - break; - case 'method': - addMethodAutoCompleteSetToContext(context); - break; - case 'body': - addBodyAutoCompleteSetToContext(context, pos); - break; - default: - return null; - } - - const isMappingsFetchingInProgress = - context.autoCompleteType === 'body' && !!context.asyncResultsState?.isLoading; - - if (!context.autoCompleteSet && !isMappingsFetchingInProgress) { - tracer('nothing to do..', context); - return null; - } - - addReplacementInfoToContext(context, pos); - - context.createdWithToken = _.clone(context.updatedForToken); - - return context; - } - - function getAutoCompleteType(pos: Position) { - // return "method", "path" or "body" to determine auto complete type. - - let rowMode = parser.getRowParseMode(); - - // eslint-disable-next-line no-bitwise - if (rowMode & parser.MODE.IN_REQUEST) { - return 'body'; - } - // eslint-disable-next-line no-bitwise - if (rowMode & parser.MODE.REQUEST_START) { - // on url path, url params or method. - const tokenIter = createTokenIterator({ - editor, - position: pos, - }); - let t = tokenIter.getCurrentToken(); - - while (t!.type === 'url.comma') { - t = tokenIter.stepBackward(); - } - switch (t!.type) { - case 'method': - return 'method'; - case 'whitespace': - t = parser.prevNonEmptyToken(tokenIter); - - switch ((t || ({} as Token)).type) { - case 'method': - // we moved one back - return 'path'; - break; - default: - if (isUrlPathToken(t)) { - return 'path'; - } - if (isUrlParamsToken(t)) { - return 'url_params'; - } - return null; - } - break; - default: - if (isUrlPathToken(t)) { - return 'path'; - } - if (isUrlParamsToken(t)) { - return 'url_params'; - } - return null; - } - } - - // after start to avoid single line url only requests - // eslint-disable-next-line no-bitwise - if (rowMode & parser.MODE.REQUEST_END) { - return 'body'; - } - - // in between request on an empty - if (editor.getLineValue(pos.lineNumber).trim() === '') { - // check if the previous line is a single line beginning of a new request - rowMode = parser.getRowParseMode(pos.lineNumber - 1); - if ( - // eslint-disable-next-line no-bitwise - rowMode & parser.MODE.REQUEST_START && - // eslint-disable-next-line no-bitwise - rowMode & parser.MODE.REQUEST_END - ) { - return 'body'; - } - // o.w suggest a method - return 'method'; - } - - return null; - } - - function addReplacementInfoToContext( - context: AutoCompleteContext, - pos: Position, - replacingTerm?: unknown - ) { - // extract the initial value, rangeToReplace & textBoxPosition - - // Scenarios for current token: - // - Nice token { "bla|" - // - Broken text token { bla| - // - No token : { | - // - Broken scenario { , bla| - // - Nice token, broken before: {, "bla" - - context.updatedForToken = _.clone( - editor.getTokenAt({ lineNumber: pos.lineNumber, column: pos.column }) - ); - if (!context.updatedForToken) { - context.updatedForToken = { - value: '', - type: '', - position: { column: pos.column, lineNumber: pos.lineNumber }, - }; - } // empty line - - let anchorToken = context.createdWithToken; - if (!anchorToken) { - anchorToken = context.updatedForToken; - } - - switch (context.updatedForToken.type) { - case 'variable': - case 'string': - case 'text': - case 'constant.numeric': - case 'constant.language.boolean': - case 'method': - case 'url.index': - case 'url.type': - case 'url.id': - case 'url.method': - case 'url.endpoint': - case 'url.part': - case 'url.param': - case 'url.value': - context.rangeToReplace = { - start: { lineNumber: pos.lineNumber, column: anchorToken.position.column }, - end: { - lineNumber: pos.lineNumber, - column: context.updatedForToken.position.column + context.updatedForToken.value.length, - }, - } as Range; - context.replacingToken = true; - break; - default: - if (replacingTerm && context.updatedForToken.value === replacingTerm) { - context.rangeToReplace = { - start: { lineNumber: pos.lineNumber, column: anchorToken.position.column }, - end: { - lineNumber: pos.lineNumber, - column: - context.updatedForToken.position.column + context.updatedForToken.value.length, - }, - } as Range; - context.replacingToken = true; - } else { - // standing on white space, quotes or another punctuation - no replacing - context.rangeToReplace = { - start: { lineNumber: pos.lineNumber, column: pos.column }, - end: { lineNumber: pos.lineNumber, column: pos.column }, - } as Range; - context.replacingToken = false; - } - break; - } - - context.textBoxPosition = { - lineNumber: context.rangeToReplace.start.lineNumber, - column: context.rangeToReplace.start.column, - }; - - switch (context.autoCompleteType) { - case 'path': - addPathPrefixSuffixToContext(context); - break; - case 'url_params': - addUrlParamsPrefixSuffixToContext(context); - break; - case 'method': - addMethodPrefixSuffixToContext(context); - break; - case 'body': - addBodyPrefixSuffixToContext(context); - break; - } - } - - function addCommaToPrefixOnAutocomplete( - nonEmptyToken: Token | null, - context: AutoCompleteContext, - charsToSkipOnSameLine: number = 1 - ) { - if (nonEmptyToken && nonEmptyToken.type.indexOf('url') < 0) { - const { position } = nonEmptyToken; - // if not on the first line - if (context.rangeToReplace && context.rangeToReplace.start?.lineNumber > 1) { - const prevTokenLineNumber = position.lineNumber; - const editorFromContext = context.editor as CoreEditor | undefined; - const line = editorFromContext?.getLineValue(prevTokenLineNumber) ?? ''; - const prevLineLength = line.length; - const linesToEnter = context.rangeToReplace.end.lineNumber - prevTokenLineNumber; - - const isTheSameLine = linesToEnter === 0; - let startColumn = prevLineLength + 1; - let spaces = context.rangeToReplace.start.column - 1; - - if (isTheSameLine) { - // prevent last char line from replacing - startColumn = position.column + charsToSkipOnSameLine; - // one char for pasted " and one for , - spaces = context.rangeToReplace.end.column - startColumn - 2; - } - - // go back to the end of the previous line - context.rangeToReplace = { - start: { lineNumber: prevTokenLineNumber, column: startColumn }, - end: { ...context.rangeToReplace.end }, - }; - - spaces = spaces >= 0 ? spaces : 0; - const spacesToEnter = isTheSameLine ? (spaces === 0 ? 1 : spaces) : spaces; - const newLineChars = `\n`.repeat(linesToEnter >= 0 ? linesToEnter : 0); - const whitespaceChars = ' '.repeat(spacesToEnter); - // add a comma at the end of the previous line, a new line and indentation - context.prefixToAdd = `,${newLineChars}${whitespaceChars}`; - } - } - } - - function addBodyPrefixSuffixToContext(context: AutoCompleteContext) { - // Figure out what happens next to the token to see whether it needs trailing commas etc. - - // Templates will be used if not destroying existing structure. - // -> token : {} or token ]/} or token , but not token : SOMETHING ELSE - - context.prefixToAdd = ''; - context.suffixToAdd = ''; - - let tokenIter = createTokenIterator({ - editor, - position: editor.getCurrentPosition()!, - }); - let nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - switch (nonEmptyToken ? nonEmptyToken.type : 'NOTOKEN') { - case 'NOTOKEN': - case 'paren.lparen': - case 'paren.rparen': - case 'punctuation.comma': - context.addTemplate = true; - break; - case 'punctuation.colon': - // test if there is an empty object - if so we replace it - context.addTemplate = false; - - nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - if (!(nonEmptyToken && nonEmptyToken.value === '{')) { - break; - } - nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - if (!(nonEmptyToken && nonEmptyToken.value === '}')) { - break; - } - context.addTemplate = true; - // extend range to replace to include all up to token - context.rangeToReplace!.end.lineNumber = tokenIter.getCurrentTokenLineNumber() as number; - context.rangeToReplace!.end.column = - (tokenIter.getCurrentTokenColumn() as number) + nonEmptyToken.value.length; - - // move one more time to check if we need a trailing comma - nonEmptyToken = parser.nextNonEmptyToken(tokenIter); - switch (nonEmptyToken ? nonEmptyToken.type : 'NOTOKEN') { - case 'NOTOKEN': - case 'paren.rparen': - case 'punctuation.comma': - case 'punctuation.colon': - break; - default: - context.suffixToAdd = ', '; - } - - break; - default: - context.addTemplate = true; - context.suffixToAdd = ', '; - break; // for now play safe and do nothing. May be made smarter. - } - - // go back to see whether we have one of ( : { & [ do not require a comma. All the rest do. - tokenIter = createTokenIterator({ editor, position: editor.getCurrentPosition() }); - nonEmptyToken = tokenIter.getCurrentToken(); - let insertingRelativeToToken; // -1 is before token, 0 middle, +1 after token - if (context.replacingToken) { - insertingRelativeToToken = 0; - } else { - const pos = editor.getCurrentPosition(); - if (pos.column === context.updatedForToken!.position.column) { - insertingRelativeToToken = -1; - } else if ( - pos.column < - context.updatedForToken!.position.column + context.updatedForToken!.value.length - ) { - insertingRelativeToToken = 0; - } else { - insertingRelativeToToken = 1; - } - } - // we should actually look at what's happening before this token - if (parser.isEmptyToken(nonEmptyToken) || insertingRelativeToToken <= 0) { - nonEmptyToken = parser.prevNonEmptyToken(tokenIter); - } - - switch (nonEmptyToken ? nonEmptyToken.type : 'NOTOKEN') { - case 'NOTOKEN': - case 'paren.lparen': - case 'punctuation.comma': - case 'punctuation.colon': - case 'punctuation.start_triple_quote': - case 'method': - break; - case 'text': - case 'string': - case 'constant.numeric': - case 'constant.language.boolean': - case 'punctuation.end_triple_quote': - addCommaToPrefixOnAutocomplete(nonEmptyToken, context, nonEmptyToken?.value.length); - break; - default: - addCommaToPrefixOnAutocomplete(nonEmptyToken, context); - break; - } - - return context; - } - - function addUrlParamsPrefixSuffixToContext(context: AutoCompleteContext) { - context.prefixToAdd = ''; - context.suffixToAdd = ''; - } - - function addMethodPrefixSuffixToContext(context: AutoCompleteContext) { - context.prefixToAdd = ''; - context.suffixToAdd = ''; - const tokenIter = createTokenIterator({ editor, position: editor.getCurrentPosition() }); - const lineNumber = tokenIter.getCurrentPosition().lineNumber; - const t = parser.nextNonEmptyToken(tokenIter); - - if (tokenIter.getCurrentPosition().lineNumber !== lineNumber || !t) { - // we still have nothing next to the method, add a space.. - context.suffixToAdd = ' '; - } - } - - function addPathPrefixSuffixToContext(context: AutoCompleteContext) { - context.prefixToAdd = ''; - context.suffixToAdd = ''; - } - - function addMethodAutoCompleteSetToContext(context: AutoCompleteContext) { - context.autoCompleteSet = ['GET', 'PUT', 'POST', 'DELETE', 'HEAD', 'PATCH'].map((m, i) => ({ - name: m, - score: -i, - meta: i18n.translate('console.autocomplete.addMethodMetaText', { defaultMessage: 'method' }), - })); - } - - function addPathAutoCompleteSetToContext(context: AutoCompleteContext, pos: Position) { - const ret = getCurrentMethodAndTokenPaths(editor, pos, parser); - context.method = ret.method?.toUpperCase(); - context.token = ret.token; - context.otherTokenValues = ret.otherTokenValues; - context.urlTokenPath = ret.urlTokenPath; - - const components = getTopLevelUrlCompleteComponents(context.method); - let urlTokenPath = context.urlTokenPath; - let predicate: (term: ResultTerm) => boolean = () => true; - - const tokenIter = createTokenIterator({ editor, position: pos }); - const currentTokenType = tokenIter.getCurrentToken()?.type; - const previousTokenType = tokenIter.stepBackward()?.type; - if (!Array.isArray(urlTokenPath)) { - // skip checks for url.comma - } else if (previousTokenType === 'url.comma' && currentTokenType === 'url.comma') { - predicate = () => false; // two consecutive commas empty the autocomplete - } else if ( - (previousTokenType === 'url.part' && currentTokenType === 'url.comma') || - (previousTokenType === 'url.slash' && currentTokenType === 'url.comma') || - (previousTokenType === 'url.comma' && currentTokenType === 'url.part') - ) { - const lastUrlTokenPath = _.last(urlTokenPath) || []; // ['c', 'd'] from 'GET /a/b/c,d,' - const constantComponents = _.filter(components, (c) => c instanceof ConstantComponent); - const constantComponentNames = _.map(constantComponents, 'name'); - - // check if neither 'c' nor 'd' is a constant component name such as '_search' - if (_.every(lastUrlTokenPath, (token) => !_.includes(constantComponentNames, token))) { - urlTokenPath = urlTokenPath.slice(0, -1); // drop the last 'c,d,' part from the url path - predicate = (term) => term.meta === 'index'; // limit the autocomplete to indices only - } - } - - populateContext(urlTokenPath, context, editor, true, components); - context.autoCompleteSet = _.filter( - addMetaToTermsList(context.autoCompleteSet!, 'endpoint'), - predicate - ); - } - - function addUrlParamsAutoCompleteSetToContext(context: AutoCompleteContext, pos: Position) { - const ret = getCurrentMethodAndTokenPaths(editor, pos, parser); - context.method = ret.method; - context.otherTokenValues = ret.otherTokenValues; - context.urlTokenPath = ret.urlTokenPath; - if (!ret.urlTokenPath) { - // zero length tokenPath is true - - return context; - } - - populateContext( - ret.urlTokenPath, - context, - editor, - false, - getTopLevelUrlCompleteComponents(context.method) - ); - - if (!context.endpoint) { - return context; - } - - if (!ret.urlParamsTokenPath) { - // zero length tokenPath is true - return context; - } - let tokenPath: string[] = []; - const currentParam = ret.urlParamsTokenPath.pop(); - if (currentParam) { - tokenPath = Object.keys(currentParam); // single key object - context.otherTokenValues = currentParam[tokenPath[0]]; - } - - populateContext( - tokenPath, - context, - editor, - true, - context.endpoint.paramsAutocomplete.getTopLevelComponents(context.method) - ); - return context; - } - - function addBodyAutoCompleteSetToContext(context: AutoCompleteContext, pos: Position) { - const ret = getCurrentMethodAndTokenPaths(editor, pos, parser); - context.method = ret.method; - context.otherTokenValues = ret.otherTokenValues; - context.urlTokenPath = ret.urlTokenPath; - context.requestStartRow = ret.requestStartRow; - if (!ret.urlTokenPath) { - // zero length tokenPath is true - return context; - } - - populateContext( - ret.urlTokenPath, - context, - editor, - false, - getTopLevelUrlCompleteComponents(context.method) - ); - - context.bodyTokenPath = ret.bodyTokenPath; - if (!ret.bodyTokenPath) { - // zero length tokenPath is true - - return context; - } - - const t = editor.getTokenAt(pos); - if (t && t.type === 'punctuation.end_triple_quote' && pos.column !== t.position.column + 3) { - // skip to populate context as the current position is not on the edge of end_triple_quote - return context; - } - - // needed for scope linking + global term resolving - context.endpointComponentResolver = getEndpointBodyCompleteComponents; - context.globalComponentResolver = getGlobalAutocompleteComponents; - let components: unknown; - if (context.endpoint) { - components = context.endpoint.bodyAutocompleteRootComponents; - } else { - components = getUnmatchedEndpointComponents(); - } - populateContext(ret.bodyTokenPath, context, editor, true, components); - - return context; - } - - const evaluateCurrentTokenAfterAChange = _.debounce(function evaluateCurrentTokenAfterAChange( - pos: Position - ) { - let currentToken = editor.getTokenAt(pos)!; - tracer('has started evaluating current token', currentToken); - - if (!currentToken) { - lastEvaluatedToken = null; - currentToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; // empty row - } - - currentToken.position.lineNumber = pos.lineNumber; // extend token with row. Ace doesn't supply it by default - if (parser.isEmptyToken(currentToken)) { - // empty token. check what's coming next - const nextToken = editor.getTokenAt({ ...pos, column: pos.column + 1 })!; - if (parser.isEmptyToken(nextToken)) { - // Empty line, or we're not on the edge of current token. Save the current position as base - currentToken.position.column = pos.column; - lastEvaluatedToken = currentToken; - } else { - nextToken.position.lineNumber = pos.lineNumber; - lastEvaluatedToken = nextToken; - } - tracer('not starting autocomplete due to empty current token'); - return; - } - - if (!lastEvaluatedToken) { - lastEvaluatedToken = currentToken; - tracer('not starting autocomplete due to invalid last evaluated token'); - return; // wait for the next typing. - } - - if (!looksLikeTypingIn(lastEvaluatedToken, currentToken, editor)) { - tracer('not starting autocomplete', lastEvaluatedToken, '->', currentToken); - // not on the same place or nothing changed, cache and wait for the next time - lastEvaluatedToken = currentToken; - return; - } - - // don't automatically open the auto complete if some just hit enter (new line) or open a parentheses - switch (currentToken.type || 'UNKNOWN') { - case 'paren.lparen': - case 'paren.rparen': - case 'punctuation.colon': - case 'punctuation.comma': - case 'comment.line': - case 'comment.punctuation': - case 'comment.block': - case 'UNKNOWN': - tracer('not starting autocomplete for current token type', currentToken.type); - return; - } - - tracer('starting autocomplete', lastEvaluatedToken, '->', currentToken); - lastEvaluatedToken = currentToken; - editor.execCommand('startAutocomplete'); - }, - 100); - - function editorChangeListener() { - const position = editor.getCurrentPosition(); - tracer('editor changed', position); - if (position && !editor.isCompleterActive()) { - tracer('will start evaluating current token'); - evaluateCurrentTokenAfterAChange(position); - } - } - - /** - * Extracts terms from the autocomplete set. - * @param context - */ - function getTerms(context: AutoCompleteContext, autoCompleteSet: ResultTerm[]) { - const terms = _.map( - autoCompleteSet.filter((term) => Boolean(term) && term.name != null), - function (term) { - if (typeof term !== 'object') { - term = { - name: term, - }; - } else { - term = _.clone(term); - } - const defaults: { - value?: string; - meta: string; - score: number; - context: AutoCompleteContext; - completer?: { insertMatch: (v: unknown) => void }; - } = { - value: term.name + '', - meta: 'API', - score: 0, - context, - }; - // we only need our custom insertMatch behavior for the body - if (context.autoCompleteType === 'body') { - defaults.completer = { - insertMatch() { - return applyTerm(term); - }, - }; - } - return _.defaults(term, defaults); - } - ); - - terms.sort(function ( - t1: { score: number; name?: string | boolean }, - t2: { score: number; name?: string | boolean } - ) { - /* score sorts from high to low */ - if (t1.score > t2.score) { - return -1; - } - if (t1.score < t2.score) { - return 1; - } - /* names sort from low to high */ - if (t1.name! < t2.name!) { - return -1; - } - if (t1.name === t2.name) { - return 0; - } - return 1; - }); - - return terms; - } - - function getSuggestions(terms: ResultTerm[]) { - return _.map(terms, function (t, i) { - t.insertValue = t.insertValue || t.value; - t.value = '' + t.value; // normalize to strings - t.score = -i; - return t; - }); - } - - function getCompletions( - position: Position, - prefix: string, - callback: (e: Error | null, result: ResultTerm[] | null) => void, - annotationControls: { - setAnnotation: (text: string) => void; - removeAnnotation: () => void; - } - ) { - try { - const context = getAutoCompleteContext(editor, position); - - if (!context) { - tracer('zero suggestions due to invalid autocomplete context'); - callback(null, []); - } else { - if (!context.asyncResultsState?.isLoading) { - const terms = getTerms(context, context.autoCompleteSet!); - const suggestions = getSuggestions(terms); - tracer(suggestions?.length ?? 0, 'suggestions'); - callback(null, suggestions); - } - - if (context.asyncResultsState) { - annotationControls.setAnnotation( - i18n.translate('console.autocomplete.fieldsFetchingAnnotation', { - defaultMessage: 'Fields fetching is in progress', - }) - ); - - context.asyncResultsState.results.then((r) => { - const asyncSuggestions = getSuggestions(getTerms(context, r)); - tracer(asyncSuggestions?.length ?? 0, 'async suggestions'); - callback(null, asyncSuggestions); - annotationControls.removeAnnotation(); - }); - } - } - } catch (e) { - // eslint-disable-next-line no-console - console.error(e); - callback(e, null); - } - } - - editor.on('changeSelection', editorChangeListener); - - return { - getCompletions, - // TODO: This needs to be cleaned up - _test: { - getCompletions: ( - _editor: unknown, - _editSession: unknown, - pos: Position, - prefix: string, - callback: (e: Error | null, result: ResultTerm[] | null) => void, - annotationControls: { - setAnnotation: (text: string) => void; - removeAnnotation: () => void; - } - ) => getCompletions(pos, prefix, callback, annotationControls), - addReplacementInfoToContext, - addChangeListener: () => editor.on('changeSelection', editorChangeListener), - removeChangeListener: () => editor.off('changeSelection', editorChangeListener), - }, - }; -} diff --git a/src/plugins/console/public/lib/autocomplete/get_endpoint_from_position.ts b/src/plugins/console/public/lib/autocomplete/get_endpoint_from_position.ts deleted file mode 100644 index b65e277e41723..0000000000000 --- a/src/plugins/console/public/lib/autocomplete/get_endpoint_from_position.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { CoreEditor, Position } from '../../types'; -import { getCurrentMethodAndTokenPaths } from './autocomplete'; -import type RowParser from '../row_parser'; - -import { getTopLevelUrlCompleteComponents } from '../kb/kb'; -import { populateContext } from './engine'; - -export function getEndpointFromPosition(editor: CoreEditor, pos: Position, parser: RowParser) { - const lineValue = editor.getLineValue(pos.lineNumber); - const context = { - ...getCurrentMethodAndTokenPaths( - editor, - { - column: lineValue.length + 1 /* Go to the very end of the line */, - lineNumber: pos.lineNumber, - }, - parser, - true - ), - }; - const components = getTopLevelUrlCompleteComponents(context.method); - populateContext(context.urlTokenPath, context, editor, true, components); - return context.endpoint; -} diff --git a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts b/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts deleted file mode 100644 index 101fd96a79024..0000000000000 --- a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import '../../application/models/sense_editor/sense_editor.test.mocks'; - -import { looksLikeTypingIn } from './looks_like_typing_in'; -import { create } from '../../application/models'; -import type { SenseEditor } from '../../application/models'; -import type { CoreEditor, Position, Token, TokensProvider } from '../../types'; - -describe('looksLikeTypingIn', () => { - let editor: SenseEditor; - let coreEditor: CoreEditor; - let tokenProvider: TokensProvider; - - beforeEach(() => { - document.body.innerHTML = `
-
-
-
-
`; - editor = create(document.getElementById('ConAppEditor')!); - coreEditor = editor.getCoreEditor(); - tokenProvider = coreEditor.getTokenProvider(); - }); - - afterEach(async () => { - await editor.update('', true); - }); - - describe('general typing in', () => { - interface RunTestArgs { - preamble: string; - autocomplete?: string; - input: string; - } - - const runTest = async ({ preamble, autocomplete, input }: RunTestArgs) => { - const pos: Position = { lineNumber: 1, column: 1 }; - - await editor.update(preamble, true); - pos.column += preamble.length; - const lastEvaluatedToken = tokenProvider.getTokenAt(pos); - - if (autocomplete !== undefined) { - await editor.update(coreEditor.getValue() + autocomplete, true); - pos.column += autocomplete.length; - } - - await editor.update(coreEditor.getValue() + input, true); - pos.column += input.length; - const currentToken = tokenProvider.getTokenAt(pos); - - expect(lastEvaluatedToken).not.toBeNull(); - expect(currentToken).not.toBeNull(); - expect(looksLikeTypingIn(lastEvaluatedToken!, currentToken!, coreEditor)).toBe(true); - }; - - const cases: RunTestArgs[] = [ - { preamble: 'G', input: 'E' }, - { preamble: 'GET .kibana', input: '/' }, - { preamble: 'GET .kibana', input: ',' }, - { preamble: 'GET .kibana', input: '?' }, - { preamble: 'GET .kibana/', input: '_' }, - { preamble: 'GET .kibana/', input: '?' }, - { preamble: 'GET .kibana,', input: '.' }, - { preamble: 'GET .kibana,', input: '?' }, - { preamble: 'GET .kibana?', input: 'k' }, - { preamble: 'GET .kibana?k', input: '=' }, - { preamble: 'GET .kibana?k=', input: 'v' }, - { preamble: 'GET .kibana?k=v', input: '&' }, - { preamble: 'GET .kibana?k', input: '&' }, - { preamble: 'GET .kibana?k&', input: 'k' }, - { preamble: 'GET ', autocomplete: '.kibana', input: '/' }, - { preamble: 'GET ', autocomplete: '.kibana', input: ',' }, - { preamble: 'GET ', autocomplete: '.kibana', input: '?' }, - { preamble: 'GET .ki', autocomplete: 'bana', input: '/' }, - { preamble: 'GET .ki', autocomplete: 'bana', input: ',' }, - { preamble: 'GET .ki', autocomplete: 'bana', input: '?' }, - { preamble: 'GET _nodes/', autocomplete: 'stats', input: '/' }, - { preamble: 'GET _nodes/sta', autocomplete: 'ts', input: '/' }, - { preamble: 'GET _nodes/', autocomplete: 'jvm', input: ',' }, - { preamble: 'GET _nodes/j', autocomplete: 'vm', input: ',' }, - { preamble: 'GET _nodes/jvm,', autocomplete: 'os', input: ',' }, - { preamble: 'GET .kibana,', autocomplete: '.security', input: ',' }, - { preamble: 'GET .kibana,.sec', autocomplete: 'urity', input: ',' }, - { preamble: 'GET .kibana,', autocomplete: '.security', input: '/' }, - { preamble: 'GET .kibana,.sec', autocomplete: 'urity', input: '/' }, - { preamble: 'GET .kibana,', autocomplete: '.security', input: '?' }, - { preamble: 'GET .kibana,.sec', autocomplete: 'urity', input: '?' }, - { preamble: 'GET .kibana/', autocomplete: '_search', input: '?' }, - { preamble: 'GET .kibana/_se', autocomplete: 'arch', input: '?' }, - { preamble: 'GET .kibana/_search?', autocomplete: 'expand_wildcards', input: '=' }, - { preamble: 'GET .kibana/_search?exp', autocomplete: 'and_wildcards', input: '=' }, - { preamble: 'GET .kibana/_search?expand_wildcards=', autocomplete: 'all', input: '&' }, - { preamble: 'GET .kibana/_search?expand_wildcards=a', autocomplete: 'll', input: '&' }, - { preamble: 'GET _cat/indices?s=index&', autocomplete: 'expand_wildcards', input: '=' }, - { preamble: 'GET _cat/indices?s=index&exp', autocomplete: 'and_wildcards', input: '=' }, - { preamble: 'GET _cat/indices?v&', autocomplete: 'expand_wildcards', input: '=' }, - { preamble: 'GET _cat/indices?v&exp', autocomplete: 'and_wildcards', input: '=' }, - // autocomplete skips one iteration of token evaluation if user types in every letter - { preamble: 'GET .kibana', autocomplete: '/', input: '_' }, // token '/' may not be evaluated - { preamble: 'GET .kibana', autocomplete: ',', input: '.' }, // token ',' may not be evaluated - { preamble: 'GET .kibana', autocomplete: '?', input: 'k' }, // token '?' may not be evaluated - ]; - for (const c of cases) { - const name = - c.autocomplete === undefined - ? `'${c.preamble}' -> '${c.input}'` - : `'${c.preamble}' -> '${c.autocomplete}' (autocomplte) -> '${c.input}'`; - test(name, async () => runTest(c)); - } - }); - - describe('first typing in', () => { - test(`'' -> 'G'`, () => { - // this is based on an implementation within the evaluateCurrentTokenAfterAChange function - const lastEvaluatedToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; - lastEvaluatedToken.position.lineNumber = coreEditor.getCurrentPosition().lineNumber; - - const currentToken = { position: { column: 1, lineNumber: 1 }, value: 'G', type: 'method' }; - expect(looksLikeTypingIn(lastEvaluatedToken, currentToken, coreEditor)).toBe(true); - }); - }); - - const matrices = [ - ` -GET .kibana/ - - -` - .slice(1, -1) - .split('\n'), - ` - - POST test/_doc -{"message": "test"} - -GET /_cat/indices?v&s= - -DE -` - .slice(1, -1) - .split('\n'), - ` - -PUT test/_doc/1 -{"field": "value"} -` - .slice(1, -1) - .split('\n'), - ]; - - describe('navigating the editor via keyboard arrow keys', () => { - const runHorizontalZigzagWalkTest = async (matrix: string[]) => { - const width = matrix[0].length; - const height = matrix.length; - - await editor.update(matrix.join('\n'), true); - let lastEvaluatedToken = tokenProvider.getTokenAt(coreEditor.getCurrentPosition()); - let currentToken: Token | null; - - for (let i = 1; i < height * width * 2; i++) { - const pos = { - column: 1 + (i % width), - lineNumber: 1 + Math.floor(i / width), - }; - if (pos.lineNumber % 2 === 0) { - pos.column = width - pos.column + 1; - } - if (pos.lineNumber > height) { - pos.lineNumber = 2 * height - pos.lineNumber + 1; - } - - currentToken = tokenProvider.getTokenAt(pos); - expect(lastEvaluatedToken).not.toBeNull(); - expect(currentToken).not.toBeNull(); - expect(looksLikeTypingIn(lastEvaluatedToken!, currentToken!, coreEditor)).toBe(false); - lastEvaluatedToken = currentToken; - } - }; - - for (const matrix of matrices) { - test(`horizontal zigzag walk ${matrix[0].length}x${matrix.length} map`, () => - runHorizontalZigzagWalkTest(matrix)); - } - }); - - describe('clicking around the editor', () => { - const runRandomClickingTest = async (matrix: string[], attempts: number) => { - const width = matrix[0].length; - const height = matrix.length; - - await editor.update(matrix.join('\n'), true); - let lastEvaluatedToken = tokenProvider.getTokenAt(coreEditor.getCurrentPosition()); - let currentToken: Token | null; - - for (let i = 1; i < attempts; i++) { - const pos = { - column: Math.ceil(Math.random() * width), - lineNumber: Math.ceil(Math.random() * height), - }; - - currentToken = tokenProvider.getTokenAt(pos); - expect(lastEvaluatedToken).not.toBeNull(); - expect(currentToken).not.toBeNull(); - expect(looksLikeTypingIn(lastEvaluatedToken!, currentToken!, coreEditor)).toBe(false); - lastEvaluatedToken = currentToken; - } - }; - - for (const matrix of matrices) { - const attempts = 4 * matrix[0].length * matrix.length; - test(`random clicking ${matrix[0].length}x${matrix.length} map ${attempts} times`, () => - runRandomClickingTest(matrix, attempts)); - } - }); -}); diff --git a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts b/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts deleted file mode 100644 index a22c985a943f6..0000000000000 --- a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { CoreEditor, Position, Token } from '../../types'; - -enum Move { - ForwardOneCharacter = 1, - ForwardOneToken, // the column position may jump to the next token by autocomplete - ForwardTwoTokens, // the column position could jump two tokens due to autocomplete -} - -const knownTypingInTokenTypes = new Map>>([ - [ - Move.ForwardOneCharacter, - new Map>([ - // a pair of the last evaluated token type and a set of the current token types - ['', new Set(['method'])], - ['url.amp', new Set(['url.param'])], - ['url.comma', new Set(['url.part', 'url.questionmark'])], - ['url.equal', new Set(['url.value'])], - ['url.param', new Set(['url.amp', 'url.equal'])], - ['url.questionmark', new Set(['url.param'])], - ['url.slash', new Set(['url.part', 'url.questionmark'])], - ['url.value', new Set(['url.amp'])], - ]), - ], - [ - Move.ForwardOneToken, - new Map>([ - ['method', new Set(['url.part'])], - ['url.amp', new Set(['url.amp', 'url.equal'])], - ['url.comma', new Set(['url.comma', 'url.questionmark', 'url.slash'])], - ['url.equal', new Set(['url.amp'])], - ['url.param', new Set(['url.equal'])], - ['url.part', new Set(['url.comma', 'url.questionmark', 'url.slash'])], - ['url.questionmark', new Set(['url.equal'])], - ['url.slash', new Set(['url.comma', 'url.questionmark', 'url.slash'])], - ['url.value', new Set(['url.amp'])], - ['whitespace', new Set(['url.comma', 'url.questionmark', 'url.slash'])], - ]), - ], - [ - Move.ForwardTwoTokens, - new Map>([['url.part', new Set(['url.param', 'url.part'])]]), - ], -]); - -const getOneCharacterNextOnTheRight = (pos: Position, coreEditor: CoreEditor): string => { - const range = { - start: { column: pos.column + 1, lineNumber: pos.lineNumber }, - end: { column: pos.column + 2, lineNumber: pos.lineNumber }, - }; - return coreEditor.getValueInRange(range); -}; - -/** - * Examines a change from the last evaluated to the current token and one - * character next to the current token position on the right. Returns true if - * the change looks like typing in, false otherwise. - * - * This function is supposed to filter out situations where autocomplete is not - * preferable, such as clicking around the editor, navigating the editor via - * keyboard arrow keys, etc. - */ -export const looksLikeTypingIn = ( - lastEvaluatedToken: Token, - currentToken: Token, - coreEditor: CoreEditor -): boolean => { - // if the column position moves to the right in the same line and the current - // token length is 1, then user is possibly typing in a character. - if ( - lastEvaluatedToken.position.column < currentToken.position.column && - lastEvaluatedToken.position.lineNumber === currentToken.position.lineNumber && - currentToken.value.length === 1 && - getOneCharacterNextOnTheRight(currentToken.position, coreEditor) === '' - ) { - const moves = - lastEvaluatedToken.position.column + 1 === currentToken.position.column - ? [Move.ForwardOneCharacter] - : [Move.ForwardOneToken, Move.ForwardTwoTokens]; - for (const move of moves) { - const tokenTypesPairs = knownTypingInTokenTypes.get(move) ?? new Map>(); - const currentTokenTypes = tokenTypesPairs.get(lastEvaluatedToken.type) ?? new Set(); - if (currentTokenTypes.has(currentToken.type)) { - return true; - } - } - } - - // if the column or the line number have changed for the last token or - // user did not provided a new value, then we should not show autocomplete - // this guards against triggering autocomplete when clicking around the editor - if ( - lastEvaluatedToken.position.column !== currentToken.position.column || - lastEvaluatedToken.position.lineNumber !== currentToken.position.lineNumber || - lastEvaluatedToken.value === currentToken.value - ) { - return false; - } - - return true; -}; diff --git a/src/plugins/console/public/lib/autocomplete_entities/autocomplete_entities.test.js b/src/plugins/console/public/lib/autocomplete_entities/autocomplete_entities.test.js index 5901c95b9a074..0cffb157abb4c 100644 --- a/src/plugins/console/public/lib/autocomplete_entities/autocomplete_entities.test.js +++ b/src/plugins/console/public/lib/autocomplete_entities/autocomplete_entities.test.js @@ -7,7 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import '../../application/models/sense_editor/sense_editor.test.mocks'; import { setAutocompleteInfo, AutocompleteInfo } from '../../services'; import { expandAliases } from './expand_aliases'; import { httpServiceMock } from '@kbn/core-http-browser-mocks'; diff --git a/src/plugins/console/public/lib/curl_parsing/__fixtures__/curl_parsing.txt b/src/plugins/console/public/lib/curl_parsing/__fixtures__/curl_parsing.txt deleted file mode 100644 index b6dd39479550d..0000000000000 --- a/src/plugins/console/public/lib/curl_parsing/__fixtures__/curl_parsing.txt +++ /dev/null @@ -1,146 +0,0 @@ -========== -Curl 1 -------------------------------------- -curl -XPUT 'http://localhost:9200/twitter/tweet/1' -d '{ - "user" : "kimchy", - "post_date" : "2009-11-15T14:12:12", - "message" : "trying out Elastic Search" -}' -------------------------------------- -PUT /twitter/tweet/1 -{ - "user" : "kimchy", - "post_date" : "2009-11-15T14:12:12", - "message" : "trying out Elastic Search" -} -========== -Curl 2 -------------------------------------- -curl -XGET "localhost/twitter/tweet/1?version=2" -d '{ - "message" : "elasticsearch now has versioning support, double cool!" -}' -------------------------------------- -GET /twitter/tweet/1?version=2 -{ - "message" : "elasticsearch now has versioning support, double cool!" -} -=========== -Curl 3 -------------------------------------- -curl -XPOST https://localhost/twitter/tweet/1?version=2 -d '{ - "message" : "elasticsearch now has versioning support, double cool!" -}' -------------------------------------- -POST /twitter/tweet/1?version=2 -{ - "message" : "elasticsearch now has versioning support, double cool!" -} -========= -Curl 4 -------------------------------------- -curl -XPOST https://localhost/twitter -------------------------------------- -POST /twitter -========== -Curl 5 -------------------------------------- -curl -X POST https://localhost/twitter/ -------------------------------------- -POST /twitter/ -============= -Curl 6 -------------------------------------- -curl -s -XPOST localhost:9200/missing-test -d' -{ - "mappings": { - } -}' -------------------------------------- -POST /missing-test -{ - "mappings": { - } -} -========================= -Curl 7 -------------------------------------- -curl 'localhost:9200/missing-test/doc/_search?pretty' -d' -{ - "query": { - }, -}' -------------------------------------- -GET /missing-test/doc/_search?pretty -{ - "query": { - }, -} -=========================== -Curl 8 -------------------------------------- -curl localhost:9200/ -d' -{ - "query": { - } -}' -------------------------------------- -GET / -{ - "query": { - } -} -==================================== -Curl Script -------------------------------------- -#!bin/sh - -// test something -curl 'localhost:9200/missing-test/doc/_search?pretty' -d' -{ - "query": { - }, -}' - - -curl -XPOST https://localhost/twitter - -#someother comments -curl localhost:9200/ -d' -{ - "query": { - } -}' - - -------------------- -# test something -GET /missing-test/doc/_search?pretty -{ - "query": { - }, -} - -POST /twitter - -#someother comments -GET / -{ - "query": { - } -} -==================================== -Curl with some text -------------------------------------- -This is what I meant: - -curl 'localhost:9200/missing-test/doc/_search?' - -This, however, does work: -curl 'localhost:9200/missing/doc/_search?' -------------------- -### This is what I meant: - -GET /missing-test/doc/_search? - -### This, however, does work: -GET /missing/doc/_search? diff --git a/src/plugins/console/public/lib/curl_parsing/curl.js b/src/plugins/console/public/lib/curl_parsing/curl.js deleted file mode 100644 index 4dd09d1b7d59b..0000000000000 --- a/src/plugins/console/public/lib/curl_parsing/curl.js +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -function detectCURLinLine(line) { - // returns true if text matches a curl request - return line.match(/^\s*?curl\s+(-X[A-Z]+)?\s*['"]?.*?['"]?(\s*$|\s+?-d\s*?['"])/); -} - -export function detectCURL(text) { - // returns true if text matches a curl request - if (!text) return false; - for (const line of text.split('\n')) { - if (detectCURLinLine(line)) { - return true; - } - } - return false; -} - -export function parseCURL(text) { - let state = 'NONE'; - const out = []; - let body = []; - let line = ''; - const lines = text.trim().split('\n'); - let matches; - - const EmptyLine = /^\s*$/; - const Comment = /^\s*(?:#|\/{2,})(.*)\n?$/; - const ExecutionComment = /^\s*#!/; - const ClosingSingleQuote = /^([^']*)'/; - const ClosingDoubleQuote = /^((?:[^\\"]|\\.)*)"/; - const EscapedQuotes = /^((?:[^\\"']|\\.)+)/; - - const LooksLikeCurl = /^\s*curl\s+/; - const CurlVerb = /-X ?(GET|HEAD|POST|PUT|DELETE|PATCH)/; - - const HasProtocol = /[\s"']https?:\/\//; - const CurlRequestWithProto = /[\s"']https?:\/\/[^\/ ]+\/+([^\s"']+)/; - const CurlRequestWithoutProto = /[\s"'][^\/ ]+\/+([^\s"']+)/; - const CurlData = /^.+\s(--data|-d)\s*/; - const SenseLine = /^\s*(GET|HEAD|POST|PUT|DELETE|PATCH)\s+\/?(.+)/; - - if (lines.length > 0 && ExecutionComment.test(lines[0])) { - lines.shift(); - } - - function nextLine() { - if (line.length > 0) { - return true; - } - if (lines.length === 0) { - return false; - } - line = lines.shift().replace(/[\r\n]+/g, '\n') + '\n'; - return true; - } - - function unescapeLastBodyEl() { - const str = body.pop().replace(/\\([\\"'])/g, '$1'); - body.push(str); - } - - // Is the next char a single or double quote? - // If so remove it - function detectQuote() { - if (line.substr(0, 1) === "'") { - line = line.substr(1); - state = 'SINGLE_QUOTE'; - } else if (line.substr(0, 1) === '"') { - line = line.substr(1); - state = 'DOUBLE_QUOTE'; - } else { - state = 'UNQUOTED'; - } - } - - // Body is finished - append to output with final LF - function addBodyToOut() { - if (body.length > 0) { - out.push(body.join('')); - body = []; - } - state = 'LF'; - out.push('\n'); - } - - // If the pattern matches, then the state is about to change, - // so add the capture to the body and detect the next state - // Otherwise add the whole line - function consumeMatching(pattern) { - const matches = line.match(pattern); - if (matches) { - body.push(matches[1]); - line = line.substr(matches[0].length); - detectQuote(); - } else { - body.push(line); - line = ''; - } - } - - function parseCurlLine() { - let verb = 'GET'; - let request = ''; - let matches; - if ((matches = line.match(CurlVerb))) { - verb = matches[1]; - } - - // JS regexen don't support possessive quantifiers, so - // we need two distinct patterns - const pattern = HasProtocol.test(line) ? CurlRequestWithProto : CurlRequestWithoutProto; - - if ((matches = line.match(pattern))) { - request = matches[1]; - } - - out.push(verb + ' /' + request + '\n'); - - if ((matches = line.match(CurlData))) { - line = line.substr(matches[0].length); - detectQuote(); - if (EmptyLine.test(line)) { - line = ''; - } - } else { - state = 'NONE'; - line = ''; - out.push(''); - } - } - - while (nextLine()) { - if (state === 'SINGLE_QUOTE') { - consumeMatching(ClosingSingleQuote); - } else if (state === 'DOUBLE_QUOTE') { - consumeMatching(ClosingDoubleQuote); - unescapeLastBodyEl(); - } else if (state === 'UNQUOTED') { - consumeMatching(EscapedQuotes); - if (body.length) { - unescapeLastBodyEl(); - } - if (state === 'UNQUOTED') { - addBodyToOut(); - line = ''; - } - } - - // the BODY state (used to match the body of a Sense request) - // can be terminated early if it encounters - // a comment or an empty line - else if (state === 'BODY') { - if (Comment.test(line) || EmptyLine.test(line)) { - addBodyToOut(); - } else { - body.push(line); - line = ''; - } - } else if (EmptyLine.test(line)) { - if (state !== 'LF') { - out.push('\n'); - state = 'LF'; - } - line = ''; - } else if ((matches = line.match(Comment))) { - out.push('#' + matches[1] + '\n'); - state = 'NONE'; - line = ''; - } else if (LooksLikeCurl.test(line)) { - parseCurlLine(); - } else if ((matches = line.match(SenseLine))) { - out.push(matches[1] + ' /' + matches[2] + '\n'); - line = ''; - state = 'BODY'; - } - - // Nothing else matches, so output with a prefix of !!! for debugging purposes - else { - out.push('### ' + line); - line = ''; - } - } - - addBodyToOut(); - return out.join('').trim(); -} diff --git a/src/plugins/console/public/lib/curl_parsing/curl_parsing.test.js b/src/plugins/console/public/lib/curl_parsing/curl_parsing.test.js deleted file mode 100644 index 80a60cd259717..0000000000000 --- a/src/plugins/console/public/lib/curl_parsing/curl_parsing.test.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import _ from 'lodash'; -import { detectCURL, parseCURL } from './curl'; -import curlTests from './__fixtures__/curl_parsing.txt'; - -describe('CURL', () => { - const notCURLS = ['sldhfsljfhs', 's;kdjfsldkfj curl -XDELETE ""', '{ "hello": 1 }']; - _.each(notCURLS, function (notCURL, i) { - test('cURL Detection - broken strings ' + i, function () { - expect(detectCURL(notCURL)).toEqual(false); - }); - }); - - curlTests.split(/^=+$/m).forEach(function (fixture) { - if (fixture.trim() === '') { - return; - } - fixture = fixture.split(/^-+$/m); - const name = fixture[0].trim(); - const curlText = fixture[1]; - const response = fixture[2].trim(); - - test('cURL Detection - ' + name, function () { - expect(detectCURL(curlText)).toBe(true); - const r = parseCURL(curlText); - expect(r).toEqual(response); - }); - }); -}); diff --git a/src/plugins/console/public/lib/kb/kb.test.js b/src/plugins/console/public/lib/kb/kb.test.js index 70ea0ef33ae86..7560789718e58 100644 --- a/src/plugins/console/public/lib/kb/kb.test.js +++ b/src/plugins/console/public/lib/kb/kb.test.js @@ -10,7 +10,6 @@ import _ from 'lodash'; import { populateContext } from '../autocomplete/engine'; -import '../../application/models/sense_editor/sense_editor.test.mocks'; import * as kb from '.'; import { AutocompleteInfo, setAutocompleteInfo } from '../../services'; diff --git a/src/plugins/console/public/lib/row_parser.test.ts b/src/plugins/console/public/lib/row_parser.test.ts deleted file mode 100644 index 869822b7bf055..0000000000000 --- a/src/plugins/console/public/lib/row_parser.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import '../application/models/legacy_core_editor/legacy_core_editor.test.mocks'; - -import RowParser from './row_parser'; -import { create, MODE } from '../application/models'; -import type { SenseEditor } from '../application/models'; -import type { CoreEditor } from '../types'; - -describe('RowParser', () => { - let editor: SenseEditor | null; - let parser: RowParser | null; - - beforeEach(function () { - // Set up our document body - document.body.innerHTML = `
-
-
-
-
`; - editor = create(document.getElementById('ConAppEditor')!); - parser = new RowParser(editor.getCoreEditor() as CoreEditor); - }); - - afterEach(function () { - editor?.getCoreEditor().destroy(); - editor = null; - parser = null; - }); - - describe('getRowParseMode', () => { - const forceRetokenize = false; - - it('should return MODE.BETWEEN_REQUESTS if line is empty', () => { - editor?.getCoreEditor().setValue('', forceRetokenize); - expect(parser?.getRowParseMode()).toBe(MODE.BETWEEN_REQUESTS); - }); - - it('should return MODE.BETWEEN_REQUESTS if line is a comment', () => { - editor?.getCoreEditor().setValue('// comment', forceRetokenize); - expect(parser?.getRowParseMode()).toBe(MODE.BETWEEN_REQUESTS); - }); - - it('should return MODE.REQUEST_START | MODE.REQUEST_END if line is a single line request', () => { - editor?.getCoreEditor().setValue('GET _search', forceRetokenize); - // eslint-disable-next-line no-bitwise - expect(parser?.getRowParseMode()).toBe(MODE.REQUEST_START | MODE.REQUEST_END); - }); - - it('should return MODE.IN_REQUEST if line is a request with an opening curly brace', () => { - editor?.getCoreEditor().setValue('{', forceRetokenize); - expect(parser?.getRowParseMode()).toBe(MODE.IN_REQUEST); - }); - - it('should return MODE.MULTI_DOC_CUR_DOC_END | MODE.IN_REQUEST if line is a multi doc request with an opening curly brace', () => { - editor?.getCoreEditor().setValue('GET _msearch\n{}\n{', forceRetokenize); - const lineNumber = editor?.getCoreEditor().getLineCount()! - 1; - expect(parser?.getRowParseMode(lineNumber)).toBe( - // eslint-disable-next-line no-bitwise - MODE.MULTI_DOC_CUR_DOC_END | MODE.IN_REQUEST - ); - }); - - it('should return MODE.MULTI_DOC_CUR_DOC_END | MODE.REQUEST_END if line is a multi doc request with a closing curly brace', () => { - editor?.getCoreEditor().setValue('GET _msearch\n{}\n{"foo": 1}\n', forceRetokenize); - const lineNumber = editor?.getCoreEditor().getLineCount()! - 1; - expect(parser?.getRowParseMode(lineNumber)).toBe( - // eslint-disable-next-line no-bitwise - MODE.MULTI_DOC_CUR_DOC_END | MODE.REQUEST_END - ); - }); - - it('should return MODE.REQUEST_START | MODE.REQUEST_END if line is a request with variables', () => { - editor?.getCoreEditor().setValue('GET /${exampleVariable}', forceRetokenize); - // eslint-disable-next-line no-bitwise - expect(parser?.getRowParseMode()).toBe(MODE.REQUEST_START | MODE.REQUEST_END); - }); - - it('should return MODE.REQUEST_START | MODE.REQUEST_END if a single request line ends with a closing curly brace', () => { - editor?.getCoreEditor().setValue('DELETE /_bar/_baz%{test}', forceRetokenize); - // eslint-disable-next-line no-bitwise - expect(parser?.getRowParseMode()).toBe(MODE.REQUEST_START | MODE.REQUEST_END); - }); - - it('should return correct modes for multiple bulk requests', () => { - editor - ?.getCoreEditor() - .setValue('POST _bulk\n{"index": {"_index": "test"}}\n{"foo": "bar"}\n', forceRetokenize); - expect(parser?.getRowParseMode(0)).toBe(MODE.BETWEEN_REQUESTS); - editor - ?.getCoreEditor() - .setValue('POST _bulk\n{"index": {"_index": "test"}}\n{"foo": "bar"}\n', forceRetokenize); - const lineNumber = editor?.getCoreEditor().getLineCount()! - 1; - expect(parser?.getRowParseMode(lineNumber)).toBe( - // eslint-disable-next-line no-bitwise - MODE.REQUEST_END | MODE.MULTI_DOC_CUR_DOC_END - ); - }); - }); -}); diff --git a/src/plugins/console/public/lib/row_parser.ts b/src/plugins/console/public/lib/row_parser.ts deleted file mode 100644 index 7078bb857d95b..0000000000000 --- a/src/plugins/console/public/lib/row_parser.ts +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { CoreEditor, Token } from '../types'; -import { TokenIterator } from './token_iterator'; - -export const MODE = { - REQUEST_START: 2, - IN_REQUEST: 4, - MULTI_DOC_CUR_DOC_END: 8, - REQUEST_END: 16, - BETWEEN_REQUESTS: 32, -}; - -// eslint-disable-next-line import/no-default-export -export default class RowParser { - constructor(private readonly editor: CoreEditor) {} - - MODE = MODE; - - getRowParseMode(lineNumber = this.editor.getCurrentPosition().lineNumber) { - const linesCount = this.editor.getLineCount(); - if (lineNumber > linesCount || lineNumber < 1) { - return MODE.BETWEEN_REQUESTS; - } - const mode = this.editor.getLineState(lineNumber); - - if (!mode) { - return MODE.BETWEEN_REQUESTS; - } // shouldn't really happen - // If another "start" mode is added here because we want to allow for new language highlighting - // please see https://github.com/elastic/kibana/pull/51446 for a discussion on why - // should consider a different approach. - if (mode !== 'start' && mode !== 'start-sql') { - return MODE.IN_REQUEST; - } - let line = (this.editor.getLineValue(lineNumber) || '').trim(); - - if (!line || line.startsWith('#') || line.startsWith('//') || line.startsWith('/*')) { - return MODE.BETWEEN_REQUESTS; - } // empty line or a comment waiting for a new req to start - - // Check for multi doc requests - if (line.endsWith('}') && !this.isRequestLine(line)) { - // check for a multi doc request must start a new json doc immediately after this one end. - lineNumber++; - if (lineNumber < linesCount + 1) { - line = (this.editor.getLineValue(lineNumber) || '').trim(); - if (line.indexOf('{') === 0) { - // next line is another doc in a multi doc - // eslint-disable-next-line no-bitwise - return MODE.MULTI_DOC_CUR_DOC_END | MODE.IN_REQUEST; - } - } - // eslint-disable-next-line no-bitwise - return MODE.REQUEST_END | MODE.MULTI_DOC_CUR_DOC_END; // end of request - } - - // check for single line requests - lineNumber++; - if (lineNumber >= linesCount + 1) { - // eslint-disable-next-line no-bitwise - return MODE.REQUEST_START | MODE.REQUEST_END; - } - line = (this.editor.getLineValue(lineNumber) || '').trim(); - if (line.indexOf('{') !== 0) { - // next line is another request - // eslint-disable-next-line no-bitwise - return MODE.REQUEST_START | MODE.REQUEST_END; - } - - return MODE.REQUEST_START; - } - - rowPredicate(lineNumber: number | undefined, editor: CoreEditor, value: number) { - const mode = this.getRowParseMode(lineNumber); - // eslint-disable-next-line no-bitwise - return (mode & value) > 0; - } - - isEndRequestRow(row?: number, _e?: CoreEditor) { - const editor = _e || this.editor; - return this.rowPredicate(row, editor, MODE.REQUEST_END); - } - - isRequestEdge(row?: number, _e?: CoreEditor) { - const editor = _e || this.editor; - // eslint-disable-next-line no-bitwise - return this.rowPredicate(row, editor, MODE.REQUEST_END | MODE.REQUEST_START); - } - - isStartRequestRow(row?: number, _e?: CoreEditor) { - const editor = _e || this.editor; - return this.rowPredicate(row, editor, MODE.REQUEST_START); - } - - isInBetweenRequestsRow(row?: number, _e?: CoreEditor) { - const editor = _e || this.editor; - return this.rowPredicate(row, editor, MODE.BETWEEN_REQUESTS); - } - - isInRequestsRow(row?: number, _e?: CoreEditor) { - const editor = _e || this.editor; - return this.rowPredicate(row, editor, MODE.IN_REQUEST); - } - - isMultiDocDocEndRow(row?: number, _e?: CoreEditor) { - const editor = _e || this.editor; - return this.rowPredicate(row, editor, MODE.MULTI_DOC_CUR_DOC_END); - } - - isEmptyToken(tokenOrTokenIter: TokenIterator | Token | null) { - const token = - tokenOrTokenIter && (tokenOrTokenIter as TokenIterator).getCurrentToken - ? (tokenOrTokenIter as TokenIterator).getCurrentToken() - : tokenOrTokenIter; - return !token || (token as Token).type === 'whitespace'; - } - - isUrlOrMethodToken(tokenOrTokenIter: TokenIterator | Token) { - const t = (tokenOrTokenIter as TokenIterator)?.getCurrentToken() ?? (tokenOrTokenIter as Token); - return t && t.type && (t.type === 'method' || t.type.indexOf('url') === 0); - } - - nextNonEmptyToken(tokenIter: TokenIterator) { - let t = tokenIter.stepForward(); - while (t && this.isEmptyToken(t)) { - t = tokenIter.stepForward(); - } - return t; - } - - prevNonEmptyToken(tokenIter: TokenIterator) { - let t = tokenIter.stepBackward(); - // empty rows return null token. - while ((t || tokenIter.getCurrentPosition().lineNumber > 1) && this.isEmptyToken(t)) - t = tokenIter.stepBackward(); - return t; - } - - isCommentToken(token: Token | null) { - return ( - token && - token.type && - (token.type === 'comment.punctuation' || - token.type === 'comment.line' || - token.type === 'comment.block') - ); - } - - isRequestLine(line: string) { - const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH', 'OPTIONS']; - return methods.some((m) => line.startsWith(m)); - } -} diff --git a/src/plugins/console/public/styles/_app.scss b/src/plugins/console/public/styles/_app.scss index 4c3ccb8b1cadc..0f0a671d920c3 100644 --- a/src/plugins/console/public/styles/_app.scss +++ b/src/plugins/console/public/styles/_app.scss @@ -36,8 +36,6 @@ width: 100%; display: flex; flex: 0 0 auto; - - // Required on IE11 to render ace editor correctly after first input. position: relative; &__spinner { @@ -55,46 +53,6 @@ height: 100%; display: flex; flex: 1 1 1px; - - .ace_badge { - font-family: $euiFontFamily; - font-size: $euiFontSizeXS; - font-weight: $euiFontWeightMedium; - line-height: $euiLineHeight; - padding: 0 $euiSizeS; - display: inline-block; - text-decoration: none; - border-radius: calc($euiBorderRadius / 2); - white-space: nowrap; - vertical-align: middle; - cursor: default; - max-width: 100%; - - &--success { - background-color: $euiColorVis0_behindText; - color: chooseLightOrDarkText($euiColorVis0_behindText); - } - - &--warning { - background-color: $euiColorVis5_behindText; - color: chooseLightOrDarkText($euiColorVis5_behindText); - } - - &--primary { - background-color: $euiColorVis1_behindText; - color: chooseLightOrDarkText($euiColorVis1_behindText); - } - - &--default { - background-color: $euiColorLightShade; - color: chooseLightOrDarkText($euiColorLightShade); - } - - &--danger { - background-color: $euiColorVis9_behindText; - color: chooseLightOrDarkText($euiColorVis9_behindText); - } - } } .conApp__editorContent, @@ -145,17 +103,6 @@ margin-inline: 0; } -// SASSTODO: This component seems to not be used anymore? -// Possibly replaced by the Ace version -.conApp__autoComplete { - position: absolute; - left: -1000px; - visibility: hidden; - /* by pass any other element in ace and resize bar, but not modal popups */ - z-index: $euiZLevel1 + 2; - margin-top: 22px; -} - .conApp__requestProgressBarContainer { position: relative; z-index: $euiZLevel2; diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index aa9bdf21c1c94..8d5ab2a582226 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -7,7 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { Editor } from 'brace'; import { ResultTerm } from '../lib/autocomplete/types'; import { TokensProvider } from './tokens_provider'; import { Token } from './token'; @@ -94,7 +93,7 @@ export enum LINE_MODE { /** * The CoreEditor is a component separate from the Editor implementation that provides Console * app specific business logic. The CoreEditor is an interface to the lower-level editor implementation - * being used which is usually vendor code such as Ace or Monaco. + * being used which is usually vendor code such as Monaco. */ export interface CoreEditor { /** @@ -260,7 +259,7 @@ export interface CoreEditor { */ registerKeyboardShortcut(opts: { keys: string | { win?: string; mac?: string }; - fn: (editor: Editor) => void; + fn: (editor: any) => void; name: string; }): void; diff --git a/src/plugins/console/tsconfig.json b/src/plugins/console/tsconfig.json index 2b0f6127cd4af..02e4e7a9b7689 100644 --- a/src/plugins/console/tsconfig.json +++ b/src/plugins/console/tsconfig.json @@ -18,10 +18,8 @@ "@kbn/i18n-react", "@kbn/shared-ux-utility", "@kbn/core-http-browser", - "@kbn/ace", "@kbn/config-schema", "@kbn/core-http-router-server-internal", - "@kbn/web-worker-stub", "@kbn/core-elasticsearch-server", "@kbn/core-http-browser-mocks", "@kbn/react-kibana-context-theme", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ea995a275449d..3b078d6bb8a90 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -320,8 +320,6 @@ "coloring.dynamicColoring.rangeType.label": "Type de valeur", "coloring.dynamicColoring.rangeType.number": "Numéro", "coloring.dynamicColoring.rangeType.percent": "Pourcent", - "console.autocomplete.addMethodMetaText": "méthode", - "console.autocomplete.fieldsFetchingAnnotation": "La récupération des champs est en cours", "console.autocompleteSuggestions.apiLabel": "API", "console.autocompleteSuggestions.endpointLabel": "point de terminaison", "console.autocompleteSuggestions.methodLabel": "méthode", @@ -362,10 +360,6 @@ "console.loadingError.title": "Impossible de charger la console", "console.notification.clearHistory": "Effacer l'historique", "console.notification.disableSavingToHistory": "Désactiver l'enregistrement", - "console.notification.error.couldNotSaveRequestTitle": "Impossible d'enregistrer la requête dans l'historique de la console.", - "console.notification.error.historyQuotaReachedMessage": "L'historique des requêtes est arrivé à saturation. Effacez l'historique de la console ou désactivez l'enregistrement de nouvelles requêtes.", - "console.notification.error.noRequestSelectedTitle": "Aucune requête sélectionnée. Sélectionnez une requête en positionnant le curseur dessus.", - "console.notification.error.unknownErrorTitle": "Erreur de requête inconnue", "console.pageHeading": "Console", "console.requestInProgressBadgeText": "Requête en cours", "console.requestOptions.autoIndentButtonLabel": "Appliquer les indentations", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2ec8bc11bc0c8..e579f87771b20 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -320,8 +320,6 @@ "coloring.dynamicColoring.rangeType.label": "値型", "coloring.dynamicColoring.rangeType.number": "Number", "coloring.dynamicColoring.rangeType.percent": "割合(%)", - "console.autocomplete.addMethodMetaText": "メソド", - "console.autocomplete.fieldsFetchingAnnotation": "フィールドの取得を実行しています", "console.autocompleteSuggestions.apiLabel": "API", "console.autocompleteSuggestions.endpointLabel": "エンドポイント", "console.autocompleteSuggestions.methodLabel": "メソド", @@ -362,10 +360,6 @@ "console.loadingError.title": "コンソールを読み込めません", "console.notification.clearHistory": "履歴を消去", "console.notification.disableSavingToHistory": "保存を無効にする", - "console.notification.error.couldNotSaveRequestTitle": "リクエストをコンソール履歴に保存できませんでした。", - "console.notification.error.historyQuotaReachedMessage": "リクエスト履歴が満杯です。コンソール履歴を消去するか、新しいリクエストの保存を無効にしてください。", - "console.notification.error.noRequestSelectedTitle": "リクエストを選択していません。リクエストの中にカーソルを置いて選択します。", - "console.notification.error.unknownErrorTitle": "不明なリクエストエラー", "console.pageHeading": "コンソール", "console.requestInProgressBadgeText": "リクエストが進行中", "console.requestOptions.autoIndentButtonLabel": "インデントを適用", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 12ee59bb6fc9c..09662465c4833 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -319,8 +319,6 @@ "coloring.dynamicColoring.rangeType.label": "值类型", "coloring.dynamicColoring.rangeType.number": "数字", "coloring.dynamicColoring.rangeType.percent": "百分比", - "console.autocomplete.addMethodMetaText": "方法", - "console.autocomplete.fieldsFetchingAnnotation": "正在提取字段", "console.autocompleteSuggestions.apiLabel": "API", "console.autocompleteSuggestions.endpointLabel": "终端", "console.autocompleteSuggestions.methodLabel": "方法", @@ -361,10 +359,6 @@ "console.loadingError.title": "无法加载控制台", "console.notification.clearHistory": "清除历史记录", "console.notification.disableSavingToHistory": "禁止保存", - "console.notification.error.couldNotSaveRequestTitle": "无法将请求保存到控制台历史记录。", - "console.notification.error.historyQuotaReachedMessage": "请求历史记录已满。请清除控制台历史记录或禁止保存新的请求。", - "console.notification.error.noRequestSelectedTitle": "未选择任何请求。将鼠标置于请求内即可选择。", - "console.notification.error.unknownErrorTitle": "未知请求错误", "console.pageHeading": "控制台", "console.requestInProgressBadgeText": "进行中的请求", "console.requestOptions.autoIndentButtonLabel": "应用行首缩进",