openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

selection.min.js (6917B)


      1 import { Pos } from "../line/pos.js"
      2 import { visualLine } from "../line/spans.js"
      3 import { getLine } from "../line/utils_line.js"
      4 import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement.js"
      5 import { getOrder, iterateBidiSections } from "../util/bidi.js"
      6 import { elt } from "../util/dom.js"
      7 
      8 export function updateSelection(cm) {
      9   cm.display.input.showSelection(cm.display.input.prepareSelection())
     10 }
     11 
     12 export function prepareSelection(cm, primary = true) {
     13   let doc = cm.doc, result = {}
     14   let curFragment = result.cursors = document.createDocumentFragment()
     15   let selFragment = result.selection = document.createDocumentFragment()
     16 
     17   for (let i = 0; i < doc.sel.ranges.length; i++) {
     18     if (!primary && i == doc.sel.primIndex) continue
     19     let range = doc.sel.ranges[i]
     20     if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue
     21     let collapsed = range.empty()
     22     if (collapsed || cm.options.showCursorWhenSelecting)
     23       drawSelectionCursor(cm, range.head, curFragment)
     24     if (!collapsed)
     25       drawSelectionRange(cm, range, selFragment)
     26   }
     27   return result
     28 }
     29 
     30 // Draws a cursor for the given range
     31 export function drawSelectionCursor(cm, head, output) {
     32   let pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
     33 
     34   let cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
     35   cursor.style.left = pos.left + "px"
     36   cursor.style.top = pos.top + "px"
     37   cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
     38 
     39   if (pos.other) {
     40     // Secondary cursor, shown when on a 'jump' in bi-directional text
     41     let otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
     42     otherCursor.style.display = ""
     43     otherCursor.style.left = pos.other.left + "px"
     44     otherCursor.style.top = pos.other.top + "px"
     45     otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
     46   }
     47 }
     48 
     49 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
     50 
     51 // Draws the given range as a highlighted selection
     52 function drawSelectionRange(cm, range, output) {
     53   let display = cm.display, doc = cm.doc
     54   let fragment = document.createDocumentFragment()
     55   let padding = paddingH(cm.display), leftSide = padding.left
     56   let rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
     57   let docLTR = doc.direction == "ltr"
     58 
     59   function add(left, top, width, bottom) {
     60     if (top < 0) top = 0
     61     top = Math.round(top)
     62     bottom = Math.round(bottom)
     63     fragment.appendChild(elt("div", null, "CodeMirror-selected", `position: absolute; left: ${left}px;
     64                              top: ${top}px; width: ${width == null ? rightSide - left : width}px;
     65                              height: ${bottom - top}px`))
     66   }
     67 
     68   function drawForLine(line, fromArg, toArg) {
     69     let lineObj = getLine(doc, line)
     70     let lineLen = lineObj.text.length
     71     let start, end
     72     function coords(ch, bias) {
     73       return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
     74     }
     75 
     76     function wrapX(pos, dir, side) {
     77       let extent = wrappedLineExtentChar(cm, lineObj, null, pos)
     78       let prop = (dir == "ltr") == (side == "after") ? "left" : "right"
     79       let ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
     80       return coords(ch, prop)[prop]
     81     }
     82 
     83     let order = getOrder(lineObj, doc.direction)
     84     iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => {
     85       let ltr = dir == "ltr"
     86       let fromPos = coords(from, ltr ? "left" : "right")
     87       let toPos = coords(to - 1, ltr ? "right" : "left")
     88 
     89       let openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen
     90       let first = i == 0, last = !order || i == order.length - 1
     91       if (toPos.top - fromPos.top <= 3) { // Single line
     92         let openLeft = (docLTR ? openStart : openEnd) && first
     93         let openRight = (docLTR ? openEnd : openStart) && last
     94         let left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
     95         let right = openRight ? rightSide : (ltr ? toPos : fromPos).right
     96         add(left, fromPos.top, right - left, fromPos.bottom)
     97       } else { // Multiple lines
     98         let topLeft, topRight, botLeft, botRight
     99         if (ltr) {
    100           topLeft = docLTR && openStart && first ? leftSide : fromPos.left
    101           topRight = docLTR ? rightSide : wrapX(from, dir, "before")
    102           botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
    103           botRight = docLTR && openEnd && last ? rightSide : toPos.right
    104         } else {
    105           topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
    106           topRight = !docLTR && openStart && first ? rightSide : fromPos.right
    107           botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
    108           botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
    109         }
    110         add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
    111         if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top)
    112         add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
    113       }
    114 
    115       if (!start || cmpCoords(fromPos, start) < 0) start = fromPos
    116       if (cmpCoords(toPos, start) < 0) start = toPos
    117       if (!end || cmpCoords(fromPos, end) < 0) end = fromPos
    118       if (cmpCoords(toPos, end) < 0) end = toPos
    119     })
    120     return {start: start, end: end}
    121   }
    122 
    123   let sFrom = range.from(), sTo = range.to()
    124   if (sFrom.line == sTo.line) {
    125     drawForLine(sFrom.line, sFrom.ch, sTo.ch)
    126   } else {
    127     let fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
    128     let singleVLine = visualLine(fromLine) == visualLine(toLine)
    129     let leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
    130     let rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
    131     if (singleVLine) {
    132       if (leftEnd.top < rightStart.top - 2) {
    133         add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
    134         add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
    135       } else {
    136         add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
    137       }
    138     }
    139     if (leftEnd.bottom < rightStart.top)
    140       add(leftSide, leftEnd.bottom, null, rightStart.top)
    141   }
    142 
    143   output.appendChild(fragment)
    144 }
    145 
    146 // Cursor-blinking
    147 export function restartBlink(cm) {
    148   if (!cm.state.focused) return
    149   let display = cm.display
    150   clearInterval(display.blinker)
    151   let on = true
    152   display.cursorDiv.style.visibility = ""
    153   if (cm.options.cursorBlinkRate > 0)
    154     display.blinker = setInterval(() => display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden",
    155       cm.options.cursorBlinkRate)
    156   else if (cm.options.cursorBlinkRate < 0)
    157     display.cursorDiv.style.visibility = "hidden"
    158 }