diff options
Diffstat (limited to 'devtools/client/shared/sourceeditor/README')
-rw-r--r-- | devtools/client/shared/sourceeditor/README | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/devtools/client/shared/sourceeditor/README b/devtools/client/shared/sourceeditor/README new file mode 100644 index 0000000000..651c2057cd --- /dev/null +++ b/devtools/client/shared/sourceeditor/README @@ -0,0 +1,238 @@ +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.58.1. 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/shared/sourceeditor + > npm install + > npm run build + +When investigating an issue in CodeMirror, you might want to have a non-minified bundle. +You can do this by running `npm run build-unminified` instead of `npm run build`. + +To confirm the functionality run mochitests for the following components: + + * sourceeditor + * debugger + * styleditor + * netmonitor + * webconsole + +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/shared/sourceeditor/test/codemirror/ +are licensed according to the contents in the LICENSE file. + +# Localization patches + +diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js +--- a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js ++++ b/devtools/client/shared/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/shared/sourceeditor/codemirror +[3] devtools/client/shared/sourceeditor/test/browser_codemirror.js +[4] devtools/client/jar.mn +[5] devtools/client/shared/sourceeditor/editor.js |