options.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import { onBlur } from "../display/focus.js"
  2. import { setGuttersForLineNumbers, updateGutters } from "../display/gutters.js"
  3. import { alignHorizontally } from "../display/line_numbers.js"
  4. import { loadMode, resetModeState } from "../display/mode_state.js"
  5. import { initScrollbars, updateScrollbars } from "../display/scrollbars.js"
  6. import { updateSelection } from "../display/selection.js"
  7. import { regChange } from "../display/view_tracking.js"
  8. import { getKeyMap } from "../input/keymap.js"
  9. import { defaultSpecialCharPlaceholder } from "../line/line_data.js"
  10. import { Pos } from "../line/pos.js"
  11. import { findMaxLine } from "../line/spans.js"
  12. import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement.js"
  13. import { replaceRange } from "../model/changes.js"
  14. import { mobile, windows } from "../util/browser.js"
  15. import { addClass, rmClass } from "../util/dom.js"
  16. import { off, on } from "../util/event.js"
  17. import { themeChanged } from "./utils.js"
  18. export let Init = {toString: function(){return "CodeMirror.Init"}}
  19. export let defaults = {}
  20. export let optionHandlers = {}
  21. export function defineOptions(CodeMirror) {
  22. let optionHandlers = CodeMirror.optionHandlers
  23. function option(name, deflt, handle, notOnInit) {
  24. CodeMirror.defaults[name] = deflt
  25. if (handle) optionHandlers[name] =
  26. notOnInit ? (cm, val, old) => {if (old != Init) handle(cm, val, old)} : handle
  27. }
  28. CodeMirror.defineOption = option
  29. // Passed to option handlers when there is no old value.
  30. CodeMirror.Init = Init
  31. // These two are, on init, called from the constructor because they
  32. // have to be initialized before the editor can start at all.
  33. option("value", "", (cm, val) => cm.setValue(val), true)
  34. option("mode", null, (cm, val) => {
  35. cm.doc.modeOption = val
  36. loadMode(cm)
  37. }, true)
  38. option("indentUnit", 2, loadMode, true)
  39. option("indentWithTabs", false)
  40. option("smartIndent", true)
  41. option("tabSize", 4, cm => {
  42. resetModeState(cm)
  43. clearCaches(cm)
  44. regChange(cm)
  45. }, true)
  46. option("lineSeparator", null, (cm, val) => {
  47. cm.doc.lineSep = val
  48. if (!val) return
  49. let newBreaks = [], lineNo = cm.doc.first
  50. cm.doc.iter(line => {
  51. for (let pos = 0;;) {
  52. let found = line.text.indexOf(val, pos)
  53. if (found == -1) break
  54. pos = found + val.length
  55. newBreaks.push(Pos(lineNo, found))
  56. }
  57. lineNo++
  58. })
  59. for (let i = newBreaks.length - 1; i >= 0; i--)
  60. replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))
  61. })
  62. option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, (cm, val, old) => {
  63. cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
  64. if (old != Init) cm.refresh()
  65. })
  66. option("specialCharPlaceholder", defaultSpecialCharPlaceholder, cm => cm.refresh(), true)
  67. option("electricChars", true)
  68. option("inputStyle", mobile ? "contenteditable" : "textarea", () => {
  69. throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
  70. }, true)
  71. option("spellcheck", false, (cm, val) => cm.getInputField().spellcheck = val, true)
  72. option("rtlMoveVisually", !windows)
  73. option("wholeLineUpdateBefore", true)
  74. option("theme", "default", cm => {
  75. themeChanged(cm)
  76. guttersChanged(cm)
  77. }, true)
  78. option("keyMap", "default", (cm, val, old) => {
  79. let next = getKeyMap(val)
  80. let prev = old != Init && getKeyMap(old)
  81. if (prev && prev.detach) prev.detach(cm, next)
  82. if (next.attach) next.attach(cm, prev || null)
  83. })
  84. option("extraKeys", null)
  85. option("configureMouse", null)
  86. option("lineWrapping", false, wrappingChanged, true)
  87. option("gutters", [], cm => {
  88. setGuttersForLineNumbers(cm.options)
  89. guttersChanged(cm)
  90. }, true)
  91. option("fixedGutter", true, (cm, val) => {
  92. cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
  93. cm.refresh()
  94. }, true)
  95. option("coverGutterNextToScrollbar", false, cm => updateScrollbars(cm), true)
  96. option("scrollbarStyle", "native", cm => {
  97. initScrollbars(cm)
  98. updateScrollbars(cm)
  99. cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
  100. cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
  101. }, true)
  102. option("lineNumbers", false, cm => {
  103. setGuttersForLineNumbers(cm.options)
  104. guttersChanged(cm)
  105. }, true)
  106. option("firstLineNumber", 1, guttersChanged, true)
  107. option("lineNumberFormatter", integer => integer, guttersChanged, true)
  108. option("showCursorWhenSelecting", false, updateSelection, true)
  109. option("resetSelectionOnContextMenu", true)
  110. option("lineWiseCopyCut", true)
  111. option("pasteLinesPerSelection", true)
  112. option("readOnly", false, (cm, val) => {
  113. if (val == "nocursor") {
  114. onBlur(cm)
  115. cm.display.input.blur()
  116. }
  117. cm.display.input.readOnlyChanged(val)
  118. })
  119. option("disableInput", false, (cm, val) => {if (!val) cm.display.input.reset()}, true)
  120. option("dragDrop", true, dragDropChanged)
  121. option("allowDropFileTypes", null)
  122. option("cursorBlinkRate", 530)
  123. option("cursorScrollMargin", 0)
  124. option("cursorHeight", 1, updateSelection, true)
  125. option("singleCursorHeightPerLine", true, updateSelection, true)
  126. option("workTime", 100)
  127. option("workDelay", 100)
  128. option("flattenSpans", true, resetModeState, true)
  129. option("addModeClass", false, resetModeState, true)
  130. option("pollInterval", 100)
  131. option("undoDepth", 200, (cm, val) => cm.doc.history.undoDepth = val)
  132. option("historyEventDelay", 1250)
  133. option("viewportMargin", 10, cm => cm.refresh(), true)
  134. option("maxHighlightLength", 10000, resetModeState, true)
  135. option("moveInputWithCursor", true, (cm, val) => {
  136. if (!val) cm.display.input.resetPosition()
  137. })
  138. option("tabindex", null, (cm, val) => cm.display.input.getField().tabIndex = val || "")
  139. option("autofocus", null)
  140. option("direction", "ltr", (cm, val) => cm.doc.setDirection(val), true)
  141. option("phrases", null)
  142. }
  143. function guttersChanged(cm) {
  144. updateGutters(cm)
  145. regChange(cm)
  146. alignHorizontally(cm)
  147. }
  148. function dragDropChanged(cm, value, old) {
  149. let wasOn = old && old != Init
  150. if (!value != !wasOn) {
  151. let funcs = cm.display.dragFunctions
  152. let toggle = value ? on : off
  153. toggle(cm.display.scroller, "dragstart", funcs.start)
  154. toggle(cm.display.scroller, "dragenter", funcs.enter)
  155. toggle(cm.display.scroller, "dragover", funcs.over)
  156. toggle(cm.display.scroller, "dragleave", funcs.leave)
  157. toggle(cm.display.scroller, "drop", funcs.drop)
  158. }
  159. }
  160. function wrappingChanged(cm) {
  161. if (cm.options.lineWrapping) {
  162. addClass(cm.display.wrapper, "CodeMirror-wrap")
  163. cm.display.sizer.style.minWidth = ""
  164. cm.display.sizerWidth = null
  165. } else {
  166. rmClass(cm.display.wrapper, "CodeMirror-wrap")
  167. findMaxLine(cm)
  168. }
  169. estimateLineHeights(cm)
  170. regChange(cm)
  171. clearCaches(cm)
  172. setTimeout(() => updateScrollbars(cm), 100)
  173. }