зеркало из https://github.com/mozilla/gecko-dev.git
01f2cedfe4
We used to rely on different things to both display the autocompletion text and accept an autocompletion. This patches introduces new helper functions in order to make the code more easy to reason about. We also rollback our decision to show the popup when there is only 1 item in the autocompletion list in order to be more consistent with what Chrome does. Differential Revision: https://phabricator.services.mozilla.com/D2824 --HG-- extra : moz-landing-system : lando |
||
---|---|---|
.. | ||
codemirror | ||
tern | ||
test | ||
.eslintrc.js | ||
README | ||
autocomplete.js | ||
css-autocompleter.js | ||
debugger.js | ||
editor-commands-controller.js | ||
editor.js | ||
moz.build | ||
package.json | ||
wasm.js | ||
webpack.config.js |
README
This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding in, it will color your code, and optionally help with indentation. # Upgrade Currently used version is 5.39.0. To upgrade: download a new version of CodeMirror from the project's page [1] and replace all JavaScript and CSS files inside the codemirror directory [2]. Then to recreate codemirror.bundle.js: > cd devtools/client/sourceeditor > npm install > webpack To confirm the functionality run mochitests for the following components: * sourceeditor * scratchpad * debugger * styleditor * netmonitor The sourceeditor component contains imported CodeMirror tests [3]. * Some tests were commented out because we don't use that functionality within Firefox (for example Ruby editing mode). Be careful when updating files test/codemirror.html and test/vimemacs.html; they were modified to co-exist with Mozilla's testing infrastructure. Basically, vimemacs.html is a copy of codemirror.html but only with VIM and Emacs mode tests enabled. * In cm_comment_test.js comment out fallbackToBlock and fallbackToLine tests. * The search addon (search.js) was slightly modified to make search UI localizable (see patch below). Other than that, we don't have any Mozilla-specific patches applied to CodeMirror itself. # Addons To install a new CodeMirror addon add it to the codemirror directory, jar.mn [4] file and editor.js [5]. Also, add it to the License section below. # License The following files in this directory and devtools/client/sourceeditor/test/codemirror/ are licensed according to the contents in the LICENSE file. # Localization patches diff --git a/devtools/client/sourceeditor/codemirror/addon/search/search.js b/devtools/client/sourceeditor/codemirror/addon/search/search.js --- a/devtools/client/sourceeditor/codemirror/addon/search/search.js +++ b/devtools/client/sourceeditor/codemirror/addon/search/search.js @@ -93,32 +93,47 @@ } else { query = parseString(query) } if (typeof query == "string" ? query == "" : query.test("")) query = /x^/; return query; } - var queryDialog = - '<span class="CodeMirror-search-label">Search:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>'; + var queryDialog; function startSearch(cm, state, query) { 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)); } } function doSearch(cm, rev, persistent, immediate) { + if (!queryDialog) { + let doc = cm.getWrapperElement().ownerDocument; + let inp = doc.createElement("input"); + + inp.type = "search"; + inp.placeholder = cm.l10n("findCmd.promptMessage"); + inp.style.marginInlineStart = "1em"; + inp.style.marginInlineEnd = "1em"; + inp.style.flexGrow = "1"; + inp.addEventListener("focus", () => inp.select()); + + queryDialog = doc.createElement("div"); + queryDialog.appendChild(inp); + queryDialog.style.display = "flex"; + } + var state = getSearchState(cm); if (state.query) return findNext(cm, rev); var q = cm.getSelection() || state.lastQuery; if (q instanceof RegExp && q.source == "x^") q = null if (persistent && cm.openDialog) { var hiding = null var searchNext = function(query, event) { CodeMirror.e_stop(event); @@ -181,56 +196,110 @@ 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 replaceQueryDialog = - ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>'; - var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>'; - var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>'; - 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); } }); } function replace(cm, all) { if (cm.getOption("readOnly")) return; var query = cm.getSelection() || getSearchState(cm).lastQuery; - var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>'; - dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) { + + let doc = cm.getWrapperElement().ownerDocument; + + // `searchLabel` is used as part of `replaceQueryFragment` and as a separate + // argument by itself, so it should be cloned. + let searchLabel = doc.createElement("span"); + searchLabel.classList.add("CodeMirror-search-label"); + searchLabel.textContent = all ? "Replace all:" : "Replace:"; + + let replaceQueryFragment = doc.createDocumentFragment(); + replaceQueryFragment.appendChild(searchLabel.cloneNode(true)); + + let searchField = doc.createElement("input"); + searchField.setAttribute("type", "text"); + searchField.setAttribute("style", "width: 10em"); + searchField.classList.add("CodeMirror-search-field"); + replaceQueryFragment.appendChild(searchField); + + let searchHint = doc.createElement("span"); + searchHint.setAttribute("style", "color: #888"); + searchHint.classList.add("CodeMirror-search-hint"); + searchHint.textContent = "(Use /re/ syntax for regexp search)"; + replaceQueryFragment.appendChild(searchHint); + + dialog(cm, replaceQueryFragment, searchLabel, query, function(query) { if (!query) return; query = parseQuery(query); - dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { + + let replacementQueryFragment = doc.createDocumentFragment(); + + let replaceWithLabel = searchLabel.cloneNode(false); + replaceWithLabel.textContent = "With:"; + replacementQueryFragment.appendChild(replaceWithLabel); + + let replaceField = doc.createElement("input"); + replaceField.setAttribute("type", "text"); + replaceField.setAttribute("style", "width: 10em"); + replaceField.classList.add("CodeMirror-search-field"); + replacementQueryFragment.appendChild(replaceField); + + dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) { text = parseString(text) if (all) { replaceAll(cm, query, text) } else { clearSearch(cm); var cursor = getSearchCursor(cm, query, cm.getCursor("from")); var advance = function() { var start = cursor.from(), match; if (!(match = cursor.findNext())) { cursor = getSearchCursor(cm, query); if (!(match = cursor.findNext()) || (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()}); - confirmDialog(cm, doReplaceConfirm, "Replace?", + cm.scrollIntoView({ from: cursor.from(), to: cursor.to() }); + + let replaceConfirmFragment = doc.createDocumentFragment(); + + let replaceConfirmLabel = searchLabel.cloneNode(false); + replaceConfirmLabel.textContent = "Replace?"; + replaceConfirmFragment.appendChild(replaceConfirmLabel); + + let yesButton = doc.createElement("button"); + yesButton.textContent = "Yes"; + replaceConfirmFragment.appendChild(yesButton); + + let noButton = doc.createElement("button"); + noButton.textContent = "No"; + replaceConfirmFragment.appendChild(noButton); + + let allButton = doc.createElement("button"); + allButton.textContent = "All"; + replaceConfirmFragment.appendChild(allButton); + + let stopButton = doc.createElement("button"); + stopButton.textContent = "Stop"; + replaceConfirmFragment.appendChild(stopButton); + + confirmDialog(cm, replaceConfirmFragment, "Replace?", [function() {doReplace(match);}, advance, function() {replaceAll(cm, query, text)}]); }; var doReplace = function(match) { cursor.replace(typeof query == "string" ? text : text.replace(/\$(\d)/g, function(_, i) {return match[i];})); advance(); }; # Footnotes [1] http://codemirror.net [2] devtools/client/sourceeditor/codemirror [3] devtools/client/sourceeditor/test/browser_codemirror.js [4] devtools/client/jar.mn [5] devtools/client/sourceeditor/editor.js