From 3b524f6aba10bb5a49bec9d109ea15798c0bed16 Mon Sep 17 00:00:00 2001 From: Sander Steenhuis Date: Fri, 9 Feb 2018 20:03:07 +0100 Subject: [PATCH] Highlight global search matches on code editor --- browser/components/CodeEditor.js | 38 ++++++++++++++++++++++++++++++++ browser/main/TopBar/index.js | 12 +++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 00dc1f6d..1e0a1ff5 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -7,6 +7,7 @@ import path from 'path' import copyImage from 'browser/main/lib/dataApi/copyImage' import { findStorage } from 'browser/lib/findStorage' import fs from 'fs' +import eventEmitter from 'browser/main/lib/eventEmitter' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' @@ -47,6 +48,39 @@ export default class CodeEditor extends React.Component { this.loadStyleHandler = (e) => { this.editor.refresh() } + this.searchHandler = (e, msg) => this.handleSearch(msg) + this.searchState = null + } + + handleSearch (msg) { + const cm = this.editor + const component = this + + if (component.searchState) cm.removeOverlay(component.searchState) + if (msg.length < 3) return + + cm.operation(function () { + component.searchState = makeOverlay(msg, 'searching') + cm.addOverlay(component.searchState) + + function makeOverlay (query, style) { + query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi') + return { + token: function (stream) { + query.lastIndex = stream.pos + var match = query.exec(stream.string) + if (match && match.index === stream.pos) { + stream.pos += match[0].length || 1 + return style + } else if (match) { + stream.pos = match.index + } else { + stream.skipToEnd() + } + } + } + } + }) } componentDidMount () { @@ -107,6 +141,9 @@ export default class CodeEditor extends React.Component { this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) this.editor.on('paste', this.pasteHandler) + eventEmitter.on('top:search', this.searchHandler) + + eventEmitter.emit('code:init') const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -126,6 +163,7 @@ export default class CodeEditor extends React.Component { this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) this.editor.off('paste', this.pasteHandler) + eventEmitter.off('top:search', this.searchHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) } diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index 11d31abd..6b4a118e 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -22,14 +22,18 @@ class TopBar extends React.Component { this.focusSearchHandler = () => { this.handleOnSearchFocus() } + + this.codeInitHandler = this.handleCodeInit.bind(this) } componentDidMount () { ee.on('top:focus-search', this.focusSearchHandler) + ee.on('code:init', this.codeInitHandler) } componentWillUnmount () { ee.off('top:focus-search', this.focusSearchHandler) + ee.off('code:init', this.codeInitHandler) } handleKeyDown (e) { @@ -73,14 +77,16 @@ class TopBar extends React.Component { handleSearchChange (e) { const { router } = this.context + const keyword = this.refs.searchInput.value if (this.state.isAlphabet || this.state.isConfirmTranslation) { router.push('/searched') } else { e.preventDefault() } this.setState({ - search: this.refs.searchInput.value + search: keyword }) + ee.emit('top:search', keyword) } handleSearchFocus (e) { @@ -115,6 +121,10 @@ class TopBar extends React.Component { } } + handleCodeInit () { + ee.emit('top:search', this.refs.searchInput.value) + } + render () { const { config, style, location } = this.props return (