首先安装自定义搜索插件
npm install --save codemirror-revisedsearch
然后新建一个codemirror-revisedsearch.js文件,因为这个搜索插件本身的样式并不符合预期,当然想要插件的就不用单独新建js文件了。
codemirror-revisedsearch.js代码:
'use strict'
var _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (obj) { return typeof obj } : function (obj) { return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj };
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Revised search plugin written by Jamie Morris
// Define search commands. Depends on advanceddialog.js
(function (mod) {
if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) == 'object' && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) == 'object') // CommonJS
{
mod(require('codemirror'), require('codemirror-advanceddialog'))
}
// else if (typeof define == 'function' && define.amd) {
// console.log('初始化2');
// define(['codemirror', 'codemirror-advanceddialog'], mod);
// }
// else {
// console.log('初始化3');
// mod(CodeMirror)
// }// Plain browser env
})(function (CodeMirror) {
'use strict'
var replaceDialog = `\n <div class="row find">\n <label for="CodeMirror-find-field">替换:</label>\n <input id="CodeMirror-find-field" type="text" class="CodeMirror-search-field" placeholder="Find" />\n <span class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>\n <span class="CodeMirror-search-count"></span>\n </div>\n <div class="row replace">\n <label for="CodeMirror-replace-field">With:</label>\n <input id="CodeMirror-replace-field" type="text" class="CodeMirror-search-field" placeholder="Replace" />\n </div>\n <div class="buttons">\n <button type="button">Find Previous</button>\n <button type="button">Find Next</button>\n <button type="button">Replace</button>\n <button type="button">Replace All</button>\n <button type="button">Close</button>\n </div>\n `
var findDialog = `\n <div class="row find">\n <label for="CodeMirror-find-field"></label>\n <input id="CodeMirror-find-field" type="text" class="CodeMirror-search-field" placeholder="请输入关键字查询" />\n <span class="CodeMirror-search-hint"></span>\n <span class="CodeMirror-search-count">0/0</span>\n </div>\n <div class="buttons">\n <button type="button"><i class="el-icon-top"></i></button>\n <button type="button"><i class="el-icon-bottom"></i></button>\n <button type="button"><i class="el-icon-close"></i></button>\n </div>\n `
var numMatches = 0
var searchOverlay = function searchOverlay(query, caseInsensitive) {
if (typeof query == 'string') query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), caseInsensitive ? 'gi' : 'g'); else if (!query.global) query = new RegExp(query.source, query.ignoreCase ? 'gi' : 'g')
return {
token: function token(stream) {
query.lastIndex = stream.pos
var match = query.exec(stream.string)
if (match && match.index == stream.pos) {
stream.pos += match[0].length || 1
return 'searching'
} else if (match) {
stream.pos = match.index
} else {
stream.skipToEnd()
}
}
}
}
function SearchState() {
this.posFrom = this.posTo = this.lastQuery = this.query = null
this.overlay = null
}
var getSearchState = function getSearchState(cm) {
return cm.state.search || (cm.state.search = new SearchState())
}
var queryCaseInsensitive = function queryCaseInsensitive(query) {
return typeof query == 'string' && query == query.toLowerCase()
}
var getSearchCursor = function getSearchCursor(cm, query, pos) {
// Heuristic: if the query string is all lowercase, do a case insensitive search.
return cm.getSearchCursor(parseQuery(query), pos, queryCaseInsensitive(query))
}
var parseString = function parseString(string) {
return string.replace(/\\(.)/g, function (_, ch) {
if (ch == 'n') return '\n'
if (ch == 'r') return '\r'
return ch
})
}
var parseQuery = function parseQuery(query) {
if (query.exec) {
return query
}
var isRE = query.indexOf('/') === 0 && query.lastIndexOf('/') > 0
if (isRE) {
try {
var matches = query.match(/^\/(.*)\/([a-z]*)$/)
query = new RegExp(matches[1], matches[2].indexOf('i') == -1 ? '' : 'i')
} catch (e) {
console.log(e)
} // Not a regular expression after all, do a string search
} else {
query = parseString(query)
}
if (typeof query == 'string' ? query == '' : query.test('')) query = /x^/
return query
}
var startSearch = function startSearch(cm, state, query) {
if (!query || query === '') return
state.queryText = query
state.query = parseQuery(query)
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query))
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query))
cm.addOverlay(state.overlay)
if (cm.showMatchesOnScrollbar) {
if (state.annotate) {
state.annotate.clear()
state.annotate = null
}
state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query))
}
}
var doSearch = function doSearch(cm, query, reverse, moveToNext) {
var hiding = null
var state = getSearchState(cm)
if (query != state.queryText) {
startSearch(cm, state, query)
state.posFrom = state.posTo = cm.getCursor()
}
if (moveToNext || moveToNext === undefined) {
findNext(cm, reverse || false)
}
updateCount(cm)
}
var clearSearch = function clearSearch(cm) {
cm.operation(function () {
var state = getSearchState(cm)
state.lastQuery = state.query
if (!state.query) return
state.query = state.queryText = null
cm.removeOverlay(state.overlay)
if (state.annotate) {
state.annotate.clear()
state.annotate = null
}
})
}
var findNext = function findNext(cm, reverse, callback) {
cm.operation(function () {
var state = getSearchState(cm)
var cursor = getSearchCursor(cm, state.query, reverse ? state.posFrom : state.posTo)
if (!cursor.find(reverse)) {
cursor = getSearchCursor(cm, state.query, reverse ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0))
if (!cursor.find(reverse)) return
}
cm.setSelection(cursor.from(), cursor.to())
cm.scrollIntoView({
from: cursor.from(),
to: cursor.to()
}, 20)
state.posFrom = cursor.from()
state.posTo = cursor.to()
if (callback) callback(cursor.from(), cursor.to())
})
}
var replaceNext = function replaceNext(cm, query, text) {
var cursor = getSearchCursor(cm, query, cm.getCursor('from'))
var start = cursor.from()
var match = cursor.findNext()
if (!match) {
cursor = getSearchCursor(cm, query)
match = cursor.findNext()
if (!match || start && cursor.from().line === start.line && cursor.from().ch === start?.ch) return
}
cm.setSelection(cursor.from(), cursor.to())
cm.scrollIntoView({
from: cursor.from(),
to: cursor.to()
})
cursor.replace(typeof query === 'string' ? text : text.replace(/$(\d)/g, function (_, i) {
return match[i]
}))
}
var replaceAll = function replaceAll(cm, query, text) {
cm.operation(function () {
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
if (typeof query != 'string') {
var match = cm.getRange(cursor.from(), cursor.to()).match(query)
cursor.replace(text.replace(/$(\d)/g, function (_, i) {
return match[i]
}))
} else cursor.replace(text)
}
})
}
var closeSearchCallback = function closeSearchCallback(cm, state) {
var states = getSearchState(cm)
states.posFrom = null
states.posTo = null
if (state.annotate) {
state.annotate.clear()
state.annotate = null
}
clearSearch(cm)
}
var getOnReadOnlyCallback = function getOnReadOnlyCallback(callback) {
var closeFindDialogOnReadOnly = function closeFindDialogOnReadOnly(cm, opt) {
if (opt === 'readOnly' && !!cm.getOption('readOnly')) {
callback()
cm.off('optionChange', closeFindDialogOnReadOnly)
}
}
return closeFindDialogOnReadOnly
}
var updateCount = function updateCount(cm) {
var state = getSearchState(cm)
var value = cm.getDoc().getValue()
var globalQuery = void 0
var queryText = state.queryText
if (!queryText || queryText === '') {
resetCount(cm)
return
}
while (queryText.charAt(queryText.length - 1) === '\\') {
queryText = queryText.substring(0, queryText.lastIndexOf('\\'))
}
if (typeof state.query === 'string') {
globalQuery = new RegExp(queryText, 'ig')
} else {
globalQuery = new RegExp(state.query.source, state.query.flags + 'g')
}
var matches = value.match(globalQuery)
var count = matches ? matches.length : 0
var countText = '0/' + count
var states = getSearchState(cm)
var searchMatches = states.annotate.matches
if (searchMatches) {
if (states.posTo === null) {
states.posTo = {
ch: cm.getCursor().ch,
line: cm.getCursor().line
}
}
var fa = searchMatches.findIndex(ha => {
return ha.to?.ch === state.posTo?.ch && ha.to.line === state.posTo.line
})
var curSearchIndex = searchMatches.length > 0 ? fa + 1 : 0
countText = curSearchIndex + '/' + count
}
cm.getWrapperElement().parentNode.querySelector('.CodeMirror-search-count').innerHTML = countText
}
var resetCount = function resetCount(cm) {
cm.getWrapperElement().parentNode.querySelector('.CodeMirror-search-count').innerHTML = '0/0'
}
var getFindBehaviour = function getFindBehaviour(cm, defaultText, callback) {
if (!defaultText) {
defaultText = ''
}
var behaviour = {
value: defaultText,
focus: true,
selectValueOnOpen: true,
closeOnEnter: false,
closeOnBlur: false,
callback: function callback(inputs, e) {
var query = inputs[0].value
if (!query) return
doSearch(cm, query, !!e.shiftKey)
},
onInput: function onInput(inputs, e) {
var query = inputs[0].value
if (!query) {
resetCount(cm)
clearSearch(cm)
return
}
doSearch(cm, query, !!e.shiftKey, false)
}
}
if (callback) {
behaviour.callback = callback
}
return behaviour
}
var getFindPrevBtnBehaviour = function getFindPrevBtnBehaviour(cm) {
return {
callback: function callback(inputs) {
var query = inputs[0].value
if (!query) return
doSearch(cm, query, true)
}
}
}
var getFindNextBtnBehaviour = function getFindNextBtnBehaviour(cm) {
return {
callback: function callback(inputs) {
var query = inputs[0].value
if (!query) return
doSearch(cm, query, false)
}
}
}
var closeBtnBehaviour = {
callback: null
}
CodeMirror.commands.find = function (cm) {
if (cm.getOption('readOnly')) return
clearSearch(cm)
var state = getSearchState(cm)
var query = cm.getSelection() || getSearchState(cm).lastQuery
var closeDialog = cm.openAdvancedDialog(findDialog, {
shrinkEditor: true,
inputBehaviours: [getFindBehaviour(cm, query)],
buttonBehaviours: [getFindPrevBtnBehaviour(cm), getFindNextBtnBehaviour(cm), closeBtnBehaviour],
onClose: function onClose() {
closeSearchCallback(cm, state)
}
})
cm.on('optionChange', getOnReadOnlyCallback(closeDialog))
startSearch(cm, state, query)
updateCount(cm)
}
CodeMirror.commands.replace = function (cm, all) {
if (cm.getOption('readOnly')) return
clearSearch(cm)
var replaceNextCallback = function replaceNextCallback(inputs) {
var query = parseQuery(inputs[0].value)
var text = parseString(inputs[1].value)
if (!query) return
replaceNext(cm, query, text)
doSearch(cm, query)
}
var state = getSearchState(cm)
var query = cm.getSelection() || state.lastQuery
var closeDialog = cm.openAdvancedDialog(replaceDialog, {
shrinkEditor: true,
inputBehaviours: [getFindBehaviour(cm, query, function (inputs) {
inputs[1].focus()
inputs[1].select()
}), {
closeOnEnter: false,
closeOnBlur: false,
callback: replaceNextCallback
}],
buttonBehaviours: [getFindPrevBtnBehaviour(cm), getFindNextBtnBehaviour(cm), {
callback: replaceNextCallback
}, {
callback: function callback(inputs) {
// Replace all
var query = parseQuery(inputs[0].value)
var text = parseString(inputs[1].value)
if (!query) return
replaceAll(cm, query, text)
}
}, closeBtnBehaviour],
onClose: function onClose() {
closeSearchCallback(cm, state)
}
})
cm.on('optionChange', getOnReadOnlyCallback(closeDialog))
startSearch(cm, state, query)
updateCount(cm)
}
})
这是在下载插件的revised-search.js基础上改的,这样我们引入的时候就不用使用插件的js文件了,而是我们自定义的js文件(第四条),想知道修改了哪里可以跟原文件进行比对。
import 'codemirror/addon/display/panel.js'
import 'codemirror/addon/search/matchesonscrollbar.js'
import 'codemirror/addon/search/matchesonscrollbar.css'
import './codemirror-revisedsearch.js'
css样式:
// codemirror 查找功能
::v-deep .CodeMirror-advanced-dialog {
position: fixed;
right: 90px;
top: 150px;
z-index: 100;
padding: 5px;
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: rgba(250, 250, 250, 1);
box-shadow: 1px 1px 12px 0 rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
.row {
width: auto;
display: flex;
align-items: center;
.CodeMirror-search-hint {
display: none;
}
}
.buttons {
margin-left: 5px;
button {
display: inline-block;
padding: 1px 3px 0px;
cursor: pointer;
border: none;
i {
font-size: 16px;
color: #3c72e7;
}
}
}
.CodeMirror-search-field {
display: block;
resize: vertical;
padding: 5px 15px;
line-height: 1.5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
font-size: inherit;
color: #1c202d;
background-color: #fff;
background-image: none;
border: 1px solid #dfdfdf;
height: 32px;
line-height: 32px;
-webkit-border-radius: 4px;
border-radius: 4px;
-webkit-transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
-o-transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&:focus {
outline: 0;
border-color: #3c72e7;
}
&::placeholder {
color: #c0c4cc;
}
}
.CodeMirror-search-count {
display: inline-block;
padding: 0 5px;
color: #1c202d;
}
}
我这里直接写css不生效,使用了::v-deep。
效果图如下: