Support editor rtl mode without wrapped lines
This commit is contained in:
parent
66766a612d
commit
6cce022c5e
|
@ -27,6 +27,10 @@
|
|||
background-color: #f7f7f7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.CodeMirror-rtl .CodeMirror-gutters {
|
||||
border-right: none;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
.CodeMirror-linenumbers {}
|
||||
.CodeMirror-linenumber {
|
||||
padding: 0 3px 0 5px;
|
||||
|
@ -165,10 +169,22 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
|||
outline: none; /* Prevent dragging from highlighting the element */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.CodeMirror-rtl .CodeMirror-scroll {
|
||||
/* 30px is the magic margin used to hide the element's real scrollbars */
|
||||
/* See overflow: hidden in .CodeMirror */
|
||||
margin-left: -30px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-sizer {
|
||||
position: relative;
|
||||
border-right: 30px solid transparent;
|
||||
}
|
||||
.CodeMirror-rtl .CodeMirror-sizer {
|
||||
border-right: 0;
|
||||
border-left: 30px solid transparent;
|
||||
}
|
||||
|
||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||
before actual scrolling happens, thus preventing shaking and
|
||||
|
@ -183,6 +199,9 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
|||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.CodeMirror-rtl .CodeMirror-vscrollbar {
|
||||
left: 0; right: initial;
|
||||
}
|
||||
.CodeMirror-hscrollbar {
|
||||
bottom: 0; left: 0;
|
||||
overflow-y: hidden;
|
||||
|
@ -191,15 +210,22 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
|||
.CodeMirror-scrollbar-filler {
|
||||
right: 0; bottom: 0;
|
||||
}
|
||||
.CodeMirror-rtl .CodeMirror-scrollbar-filler {
|
||||
right: initial; left: 0;
|
||||
}
|
||||
.CodeMirror-gutter-filler {
|
||||
left: 0; bottom: 0;
|
||||
}
|
||||
.CodeMirror-rtl .CodeMirror-gutter-filler {
|
||||
left: initial; right: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute; left: 0; top: 0;
|
||||
min-height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
.CodeMirror-rtl .CodeMirror-gutters { left: initial }
|
||||
.CodeMirror-gutter {
|
||||
white-space: normal;
|
||||
height: 100%;
|
||||
|
@ -269,7 +295,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
|||
|
||||
.CodeMirror-widget {}
|
||||
|
||||
.CodeMirror-rtl pre { direction: rtl; }
|
||||
.CodeMirror-rtl { direction: rtl; }
|
||||
|
||||
.CodeMirror-code {
|
||||
outline: none;
|
||||
|
|
|
@ -9,21 +9,52 @@ import { updateGutterSpace } from "./update_display"
|
|||
export function alignHorizontally(cm) {
|
||||
let display = cm.display, view = display.view
|
||||
if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return
|
||||
let comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
|
||||
let gutterW = display.gutters.offsetWidth, left = comp + "px"
|
||||
let isLtr = cm.doc.direction == "ltr"
|
||||
let scroll = display.scroller.scrollLeft - cm.doc.scrollLeft
|
||||
let comp = compensateForHScroll(display, isLtr) + (isLtr ? -scroll : scroll)
|
||||
let offset = comp + "px"
|
||||
let side = isLtr ? "left" : "right"
|
||||
let otherSide = isLtr ? "right" : "left"
|
||||
for (let i = 0; i < view.length; i++) if (!view[i].hidden) {
|
||||
if (cm.options.fixedGutter) {
|
||||
if (view[i].gutter)
|
||||
view[i].gutter.style.left = left
|
||||
if (view[i].gutterBackground)
|
||||
view[i].gutterBackground.style.left = left
|
||||
if (view[i].gutter) {
|
||||
view[i].gutter.style[side] = offset
|
||||
view[i].gutter.style[otherSide] = null
|
||||
}
|
||||
if (view[i].gutterBackground) {
|
||||
view[i].gutterBackground.style[side] = offset
|
||||
view[i].gutterBackground.style[otherSide] = null
|
||||
}
|
||||
}
|
||||
let align = view[i].alignable
|
||||
if (align) for (let j = 0; j < align.length; j++)
|
||||
align[j].style.left = left
|
||||
if (align) for (let j = 0; j < align.length; j++) {
|
||||
align[j].style[side] = offset
|
||||
align[j].style[otherSide] = null
|
||||
}
|
||||
}
|
||||
if (cm.options.fixedGutter)
|
||||
display.gutters.style.left = (comp + gutterW) + "px"
|
||||
setGutterOffset(cm)
|
||||
}
|
||||
|
||||
function setGutterOffset(cm, fixed = cm.options.fixedGutter, alsoIfNotFixed = false) {
|
||||
let isLtr = cm.doc.direction == "ltr"
|
||||
let side = isLtr ? "left" : "right"
|
||||
let display = cm.display
|
||||
if (!fixed) {
|
||||
if (alsoIfNotFixed) {
|
||||
display.gutters.style[side] = "0"
|
||||
display.gutters.style[isLtr ? "right" : "left"] = null
|
||||
}
|
||||
return
|
||||
}
|
||||
let scroll = display.scroller.scrollLeft - cm.doc.scrollLeft
|
||||
let comp = compensateForHScroll(display, isLtr) + (isLtr ? -scroll : scroll)
|
||||
let gutterW = display.gutters.offsetWidth
|
||||
display.gutters.style[side] = (comp + gutterW) + "px"
|
||||
display.gutters.style[isLtr ? "right" : "left"] = null
|
||||
}
|
||||
|
||||
export function updateFixedGutter(cm, val) {
|
||||
setGutterOffset(cm, val, true)
|
||||
}
|
||||
|
||||
// Used to ensure that the line number gutter is still the right
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { clipPos } from "../line/pos"
|
||||
import { findMaxLine } from "../line/spans"
|
||||
import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement"
|
||||
import { gecko } from "../util/browser"
|
||||
import { signal } from "../util/event"
|
||||
import { activeElt } from "../util/dom"
|
||||
import { finishOperation, pushOperation } from "../util/operation_group"
|
||||
|
@ -100,7 +101,7 @@ function endOperation_R2(op) {
|
|||
cm.display.sizerWidth = op.adjustWidthTo
|
||||
op.barMeasure.scrollWidth =
|
||||
Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
|
||||
op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
|
||||
op.maxScrollLeft = Math[gecko && cm.doc.direction == "rtl" ? "min" : "max"](0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
|
||||
}
|
||||
|
||||
if (op.updatedDisplay || op.selectionChanged)
|
||||
|
|
|
@ -65,8 +65,8 @@ class NativeScrollbars {
|
|||
|
||||
if (needsH) {
|
||||
this.horiz.style.display = "block"
|
||||
this.horiz.style.right = needsV ? sWidth + "px" : "0"
|
||||
this.horiz.style.left = measure.barLeft + "px"
|
||||
this.horiz.style[this.cm.doc.direction == "ltr" ? "right" : "left"] = needsV ? sWidth + "px" : "0"
|
||||
this.horiz.style[this.cm.doc.direction == "ltr" ? "left" : "right"] = measure.barLeft + "px"
|
||||
let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
|
||||
this.horiz.firstChild.style.width =
|
||||
Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
|
||||
|
@ -150,7 +150,8 @@ function updateScrollbarsInner(cm, measure) {
|
|||
let d = cm.display
|
||||
let sizes = d.scrollbars.update(measure)
|
||||
|
||||
d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
|
||||
d.sizer.style[cm.doc.direction == "ltr" ? "paddingRight" : "paddingLeft"] = (d.barWidth = sizes.right) + "px"
|
||||
d.sizer.style[cm.doc.direction == "ltr" ? "paddingLeft" : "paddingRight"] = 0
|
||||
d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
|
||||
d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
|
||||
|
||||
|
|
|
@ -86,10 +86,11 @@ export function calculateScrollPos(cm, rect) {
|
|||
let screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
|
||||
let tooWide = rect.right - rect.left > screenw
|
||||
if (tooWide) rect.right = rect.left + screenw
|
||||
if (rect.left < 10)
|
||||
let rtl = cm.doc.direction == "rtl"
|
||||
if (Math.abs(rect.left) < 10 || (rtl ? rect.left > 0 : rect.left < 0))
|
||||
result.scrollLeft = 0
|
||||
else if (rect.left < screenleft)
|
||||
result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10))
|
||||
result.scrollLeft = Math[rtl ? "min" : "max"](0, rect.left - (tooWide ? 0 : 10))
|
||||
else if (rect.right > screenw + screenleft - 3)
|
||||
result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw
|
||||
return result
|
||||
|
|
|
@ -92,7 +92,7 @@ function drawSelectionRange(cm, range, output) {
|
|||
start = leftPos
|
||||
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
|
||||
end = rightPos
|
||||
if (left < leftSide + 1) left = leftSide
|
||||
if (left < leftSide + 1 && cm.doc.direction != "rtl") left = leftSide
|
||||
add(left, rightPos.top, right - left, rightPos.bottom)
|
||||
})
|
||||
return {start: start, end: end}
|
||||
|
|
|
@ -49,7 +49,8 @@ export function maybeClipScrollbars(cm) {
|
|||
display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
|
||||
display.heightForcer.style.height = scrollGap(cm) + "px"
|
||||
display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
|
||||
display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
|
||||
display.sizer.style[cm.doc.direction == "ltr" ? "borderLeftWidth" : "borderRightWidth"] = null
|
||||
display.sizer.style[cm.doc.direction == "ltr" ? "borderRightWidth" : "borderLeftWidth"] = scrollGap(cm) + "px"
|
||||
display.scrollbarsClipped = true
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +220,8 @@ function patchDisplay(cm, updateNumbersFrom, dims) {
|
|||
|
||||
export function updateGutterSpace(cm) {
|
||||
let width = cm.display.gutters.offsetWidth
|
||||
cm.display.sizer.style.marginLeft = width + "px"
|
||||
cm.display.sizer.style[cm.doc.direction == "ltr" ? "marginRight" : "marginLeft"] = null
|
||||
cm.display.sizer.style[cm.doc.direction == "ltr" ? "marginLeft" : "marginRight"] = width + "px"
|
||||
}
|
||||
|
||||
export function setDocumentHeight(cm, measure) {
|
||||
|
|
|
@ -93,17 +93,18 @@ function updateLineGutter(cm, lineView, lineN, dims) {
|
|||
lineView.node.removeChild(lineView.gutterBackground)
|
||||
lineView.gutterBackground = null
|
||||
}
|
||||
let side = (cm.doc.direction == "ltr" ? "left" : "right")
|
||||
if (lineView.line.gutterClass) {
|
||||
let wrap = ensureLineWrapped(lineView)
|
||||
lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
|
||||
`left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`)
|
||||
`${side}: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`)
|
||||
cm.display.input.setUneditable(lineView.gutterBackground)
|
||||
wrap.insertBefore(lineView.gutterBackground, lineView.text)
|
||||
}
|
||||
let markers = lineView.line.gutterMarkers
|
||||
if (cm.options.lineNumbers || markers) {
|
||||
let wrap = ensureLineWrapped(lineView)
|
||||
let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`)
|
||||
let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `${side}: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`)
|
||||
cm.display.input.setUneditable(gutterWrap)
|
||||
wrap.insertBefore(gutterWrap, lineView.text)
|
||||
if (lineView.line.gutterClass)
|
||||
|
@ -112,12 +113,12 @@ function updateLineGutter(cm, lineView, lineN, dims) {
|
|||
lineView.lineNumber = gutterWrap.appendChild(
|
||||
elt("div", lineNumberFor(cm.options, lineN),
|
||||
"CodeMirror-linenumber CodeMirror-gutter-elt",
|
||||
`left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`))
|
||||
`${side}: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`))
|
||||
if (markers) for (let k = 0; k < cm.options.gutters.length; ++k) {
|
||||
let id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
|
||||
if (found)
|
||||
gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
|
||||
`left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`))
|
||||
`${side}: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,9 +278,10 @@ function leftButtonSelect(cm, e, start, type, addNew) {
|
|||
// handlers for the corresponding event.
|
||||
function gutterEvent(cm, e, type, prevent) {
|
||||
let mX, mY
|
||||
let check = cm.doc.direction == "ltr" ? dom => mX >= Math.floor(dom.getBoundingClientRect().right) : dom => mX <= Math.floor(dom.getBoundingClientRect().left)
|
||||
try { mX = e.clientX; mY = e.clientY }
|
||||
catch(e) { return false }
|
||||
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false
|
||||
if (check(cm.display.gutters)) return false
|
||||
if (prevent) e_preventDefault(e)
|
||||
|
||||
let display = cm.display
|
||||
|
@ -291,7 +292,7 @@ function gutterEvent(cm, e, type, prevent) {
|
|||
|
||||
for (let i = 0; i < cm.options.gutters.length; ++i) {
|
||||
let g = display.gutters.childNodes[i]
|
||||
if (g && g.getBoundingClientRect().right >= mX) {
|
||||
if (g && check(g)) {
|
||||
let line = lineAtHeight(cm.doc, mY)
|
||||
let gutter = cm.options.gutters[i]
|
||||
signal(cm, type, cm, line, gutter, e)
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { onBlur } from "../display/focus"
|
||||
import { setGuttersForLineNumbers, updateGutters } from "../display/gutters"
|
||||
import { alignHorizontally } from "../display/line_numbers"
|
||||
import { alignHorizontally, updateFixedGutter } from "../display/line_numbers"
|
||||
import { loadMode, resetModeState } from "../display/mode_state"
|
||||
import { initScrollbars, updateScrollbars } from "../display/scrollbars"
|
||||
import { setScrollLeft } from "../display/scroll_events"
|
||||
import { updateSelection } from "../display/selection"
|
||||
import { regChange } from "../display/view_tracking"
|
||||
import { getKeyMap } from "../input/keymap"
|
||||
import { defaultSpecialCharPlaceholder } from "../line/line_data"
|
||||
import { Pos } from "../line/pos"
|
||||
import { findMaxLine } from "../line/spans"
|
||||
import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement"
|
||||
import { clearCaches, estimateLineHeights } from "../measurement/position_measurement"
|
||||
import { replaceRange } from "../model/changes"
|
||||
import { mobile, windows } from "../util/browser"
|
||||
import { addClass, rmClass } from "../util/dom"
|
||||
|
@ -99,7 +100,7 @@ export function defineOptions(CodeMirror) {
|
|||
guttersChanged(cm)
|
||||
}, true)
|
||||
option("fixedGutter", true, (cm, val) => {
|
||||
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
|
||||
updateFixedGutter(cm, val)
|
||||
cm.refresh()
|
||||
}, true)
|
||||
option("coverGutterNextToScrollbar", false, cm => updateScrollbars(cm), true)
|
||||
|
@ -153,7 +154,13 @@ export function defineOptions(CodeMirror) {
|
|||
|
||||
option("tabindex", null, (cm, val) => cm.display.input.getField().tabIndex = val || "")
|
||||
option("autofocus", null)
|
||||
option("direction", "ltr", (cm, val) => cm.doc.setDirection(val), true)
|
||||
option("direction", "ltr", (cm, val) => {
|
||||
cm.doc.setDirection(val)
|
||||
cm.display.scroller.scrollLeft = 0 // Chromium needs this
|
||||
alignHorizontally(cm)
|
||||
setScrollLeft(cm, 0)
|
||||
guttersChanged(cm)
|
||||
}, true)
|
||||
}
|
||||
|
||||
function guttersChanged(cm) {
|
||||
|
|
|
@ -419,7 +419,7 @@ export function coordsChar(cm, x, y) {
|
|||
let lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
|
||||
if (lineN > last)
|
||||
return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1)
|
||||
if (x < 0) x = 0
|
||||
if (x < 0 && cm.doc.direction != "rtl") x = 0
|
||||
|
||||
let lineObj = getLine(doc, lineN)
|
||||
for (;;) {
|
||||
|
@ -537,7 +537,7 @@ export function getDimensions(cm) {
|
|||
left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
|
||||
width[cm.options.gutters[i]] = n.clientWidth
|
||||
}
|
||||
return {fixedPos: compensateForHScroll(d),
|
||||
return {fixedPos: compensateForHScroll(d, cm.doc.direction == "ltr"),
|
||||
gutterTotalWidth: d.gutters.offsetWidth,
|
||||
gutterLeft: left,
|
||||
gutterWidth: width,
|
||||
|
@ -547,8 +547,10 @@ export function getDimensions(cm) {
|
|||
// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
|
||||
// but using getBoundingClientRect to get a sub-pixel-accurate
|
||||
// result.
|
||||
export function compensateForHScroll(display) {
|
||||
return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
|
||||
export function compensateForHScroll(display, isLtr) {
|
||||
let side = isLtr ? "left" : "right"
|
||||
let diff = display.scroller.getBoundingClientRect()[side] - display.sizer.getBoundingClientRect()[side]
|
||||
return isLtr ? diff : -diff
|
||||
}
|
||||
|
||||
// Returns a function that estimates the height of a line, to use as
|
||||
|
|
|
@ -100,7 +100,7 @@ export function attachDoc(cm, doc) {
|
|||
}
|
||||
|
||||
function setDirectionClass(cm) {
|
||||
;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
|
||||
;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.wrapper, "CodeMirror-rtl")
|
||||
}
|
||||
|
||||
export function directionChanged(cm) {
|
||||
|
|
Loading…
Reference in New Issue