| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 |
- import { EditorSelection, Extension } from '@codemirror/state'
- import {
- BlockInfo,
- EditorView,
- lineNumbers as _lineNumbers,
- } from '@codemirror/view'
- import { DebouncedFunc, throttle } from 'lodash'
- /**
- * The built-in extension which displays line numbers in the gutter,
- * configured with a mousedown/mouseup handler that selects lines of the document
- * when dragging a selection in the gutter.
- */
- export function lineNumbers(): Extension {
- let listener: DebouncedFunc<(event: MouseEvent) => boolean> | null
- function disableListener() {
- if (listener) {
- document.removeEventListener('mousemove', listener)
- listener = null
- }
- }
- // Creates a selection range capped within the document bounds. The range is
- // anchored at the beginning so that it is a full line that is selected
- function selection(view: EditorView, start: BlockInfo, end: BlockInfo) {
- const clamp = (num: number) =>
- Math.max(0, Math.min(view.state.doc.length, num))
- let startPos = start.from
- let endPos = end.to + 1
- if (start.from === end.from) {
- // Selecting one line
- startPos = end.to + 1
- endPos = start.from
- } else if (end.from < start.from) {
- // End is prior to start
- endPos = end.from
- startPos = start.to + 1
- }
- return EditorSelection.range(clamp(startPos), clamp(endPos))
- }
- // Wrapper around the built-in codemirror lineNumbers() extension
- return _lineNumbers({
- domEventHandlers: {
- mousedown: (view, line, event) => {
- // Disable default focusing of line number
- event.preventDefault()
- // If we already have a listener, disable it
- disableListener()
- view.dispatch({
- selection: selection(view, line, line),
- })
- // Focus the editor
- view.contentDOM.focus()
- // Set up new listener to track the mouse position
- listener = throttle((event: MouseEvent) => {
- // Check if we've missed a mouseup event by validating that the
- // primary mouse button is still being held
- if (event.buttons !== 1) {
- disableListener()
- return false
- }
- // Map the mouse cursor to the document, and select the lines matched
- const documentPosition = view.posAtCoords({
- x: event.pageX,
- y: event.pageY,
- })
- if (documentPosition) {
- const endLine = view.lineBlockAt(documentPosition)
- view.dispatch({
- selection: selection(view, line, endLine),
- })
- }
- }, 50)
- document.addEventListener('mousemove', listener)
- return false
- },
- mouseup: () => {
- disableListener()
- return false
- },
- },
- })
- }
|