diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index a0c3cfff..4845116a 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -32,6 +32,7 @@ "graphql-request": "^3.7.0", "idb": "^7.0.0", "immer": "^9.0.15", + "know-your-http-well": "^0.5.0", "lodash": "^4.17.21", "markdown-it": "^13.0.1", "mousetrap": "^1.6.5", diff --git a/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js b/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js index 2cd5ccad..a36004c2 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js +++ b/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js @@ -8,6 +8,8 @@ import { addRequestHeader, updateRequestHeader, deleteRequestHeader } from 'prov import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import SingleLineEditor from 'components/SingleLineEditor'; import StyledWrapper from './StyledWrapper'; +import { headers as StandardHTTPHeaders } from 'know-your-http-well'; +const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header); const RequestHeaders = ({ item, collection }) => { const dispatch = useDispatch(); @@ -91,6 +93,7 @@ const RequestHeaders = ({ item, collection }) => { 'name' ) } + autocomplete={headerAutoCompleteList} onRun={handleRun} collection={collection} /> diff --git a/packages/bruno-app/src/components/SingleLineEditor/index.js b/packages/bruno-app/src/components/SingleLineEditor/index.js index 5c4ba12d..3b786ca9 100644 --- a/packages/bruno-app/src/components/SingleLineEditor/index.js +++ b/packages/bruno-app/src/components/SingleLineEditor/index.js @@ -9,6 +9,35 @@ const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODE if (!SERVER_RENDERED) { CodeMirror = require('codemirror'); + CodeMirror.registerHelper('hint', 'anyword', (editor, options) => { + const word = /[\w$]+/; + const wordlist = (options && options.autocomplete) || []; + let cur = editor.getCursor(), + curLine = editor.getLine(cur.line); + let end = cur.ch, + start = end; + while (start && word.test(curLine.charAt(start - 1))) --start; + let curWord = start != end && curLine.slice(start, end); + + const list = (options && options.list) || []; + const re = new RegExp(word.source, 'g'); + for (let dir = -1; dir <= 1; dir += 2) { + let line = cur.line, + endLine = Math.min(Math.max(line + dir * 500, editor.firstLine()), editor.lastLine()) + dir; + for (; line != endLine; line += dir) { + let text = editor.getLine(line), + m; + while ((m = re.exec(text))) { + if (line == cur.line && curWord.length < 3) continue; + list.push(...wordlist.filter((el) => el.toLowerCase().startsWith(curWord.toLowerCase()))); + } + } + } + return { list: [...new Set(list)], from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end) }; + }); + CodeMirror.commands.autocomplete = (cm, hint, options) => { + cm.showHint({ hint, ...options }); + }; } class SingleLineEditor extends Component { @@ -73,6 +102,14 @@ class SingleLineEditor extends Component { Tab: () => {} } }); + if (this.props.autocomplete) { + this.editor.on('keyup', (cm, event) => { + if (!cm.state.completionActive /*Enables keyboard navigation in autocomplete list*/ && event.keyCode != 13) { + /*Enter - do not open autocomplete list just after item has been selected in it*/ + CodeMirror.commands.autocomplete(cm, CodeMirror.hint.anyword, { autocomplete: this.props.autocomplete }); + } + }); + } this.editor.setValue(this.props.value || ''); this.editor.on('change', this._onEdit); this.addOverlay();