Skip to content

Commit

Permalink
feat(editor): improve ordered list handling and outdent functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
purocean committed Jan 2, 2025
1 parent 660a53b commit 1fc1cb7
Showing 1 changed file with 69 additions and 15 deletions.
84 changes: 69 additions & 15 deletions src/renderer/plugins/editor-md-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import type { Plugin } from '@fe/context'
import { getSetting } from '@fe/services/setting'
import { isKeydown } from '@fe/core/keybinding'

const emptyItemReg = /^\s*(?:[*+\->]|\d+[.)]|[*+-] \[ \])\s*$/
const unorderedListReg = /^\s*[*+-]\s/
const orderedListReg = /^(\s*)(\d+)([.)])(\s)/
const emptyLineReg = /^\s*$/

let ignoreTabProcess = false

function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monaco: typeof Monaco, e: Monaco.editor.ICursorPositionChangedEvent) {
Expand All @@ -13,7 +18,6 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac
}

const source = 'list-completion'
const emptyItemReg = /^\s*(?:[*+\->]|\d+[.)]|[*+-] \[ \])\s*$/
const isTab = e.source === 'tab'
const isOutdent = e.source === 'outdent'
const isDeleteLeft = e.source === 'deleteLeft'
Expand Down Expand Up @@ -101,7 +105,6 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac
}

const maxLineCount = model.getLineCount()
const reg = /^(\s*)(\d+)([.)])(\s)/

const processOrderedList = (position: Monaco.Position) => {
const line = position.lineNumber
Expand All @@ -117,7 +120,7 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac
const prefix = currentLine.slice(0, position.column - 1)

// check current line is an ordered list item
const match = currentLine.match(reg)
const match = currentLine.match(orderedListReg)
if (!match) {
return
}
Expand All @@ -134,14 +137,42 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac
return
}

// 1. outdent a <- break
// 1. aaa <- matched
// 2. bbb <- matched
// 1. bbb.aaa <- matched
// 2. bbb.bbb <- matched
// 3. ccc <- matched
// CCC <- matched
// 4. ddd <- [*]cursor here
// 5. eee <- matched
// 1. eee.aaa <- matched
// 6. ddd <- matched
// 2. outdent b <- break
const matchContent = (content: string) => {
if (emptyLineReg.test(content)) { // empty line
return false
}

if (unorderedListReg.test(content)) { // unordered list
return false
}

const m = content.match(orderedListReg)
if (m && m[1].length < indent.length) { // outdent
return false
}

return true
}

while (startLine > 0) {
const content = model.getLineContent(startLine)

const m = content.match(reg)
if ((!m && content.trim().length === 0) || (m && m[1].length < indent.length)) {
if (!matchContent(content)) {
break
}

// if we are here, it means the line is ordered list item (same or deeper indent)
prevLines.unshift(content)
startLine--
}
Expand All @@ -150,12 +181,11 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac

while (endLine <= maxLineCount) {
const content = model.getLineContent(endLine)

const m = content.match(reg)
if ((!m && content.trim().length === 0) || (m && m[1].length < indent.length)) {
if (!matchContent(content)) {
break
}

// if we are here, it means the line is ordered list item (same or deeper indent)
nextLines.push(content)
endLine++
}
Expand All @@ -168,20 +198,20 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac
}

const firstLine = prevLines[0]
const firstMatch = firstLine ? firstLine.match(reg) : null
const firstMatch = firstLine ? firstLine.match(orderedListReg) : null
const startNum = firstMatch ? parseInt(firstMatch[2]) : 1
let shouldInc = orderedListCompletion === 'increase'

if (orderedListCompletion === 'auto') {
const secondLine = prevLines[1]
const secondMatch = secondLine ? secondLine.match(reg) : null
const secondMatch = secondLine ? secondLine.match(orderedListReg) : null
shouldInc = secondMatch ? parseInt(secondMatch[2]) > 1 : false
}

const changedLines: number[] = []
let seq = 0
const text = [...prevLines, currentLine, ...nextLines].map((line, i) => {
const replaced = line.replace(reg, (s, p1, p2, p3, p4) => {
const replaced = line.replace(orderedListReg, (s, p1, p2, p3, p4) => {
if (p1 !== indent) {
return s
}
Expand Down Expand Up @@ -234,9 +264,33 @@ function processCursorChange (editor: Monaco.editor.IStandaloneCodeEditor, monac

doEdits(e.position)

// if press tab key, auto complete the next line
if ((isTab || isOutdent) && model.getLineCount() > e.position.lineNumber) {
doEdits(new monaco.Position(e.position.lineNumber + 1, 1))
// if press tab key, auto complete the next outdent line
if ((isTab || isOutdent)) {
// find the next outdent line
let outdentLine: number = 0
const lineCount = model.getLineCount()
const currentLine = model.getLineContent(e.position.lineNumber)
const currentLineIndent = currentLine.match(orderedListReg)?.[1]

if (typeof currentLineIndent !== 'string') return

let line = e.position.lineNumber + 1
while (line < lineCount) {
const content = model.getLineContent(line)
const m = content.match(orderedListReg)
if (!m) return

if (m[1].length < currentLineIndent.length) {
outdentLine = line
break
}

line++
}

if (outdentLine) {
doEdits(new monaco.Position(outdentLine, 1))
}
}
}

Expand Down

0 comments on commit 1fc1cb7

Please sign in to comment.