aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorHsieh Chin Fan <pham@topo.tw>2024-09-13 21:20:37 +0800
committerHsieh Chin Fan <pham@topo.tw>2024-09-15 00:27:48 +0800
commite84ec00be6416c8dd1578d5a4589fa59ea1d92ee (patch)
tree6001e220e17f2f739124cdd6c333e3c2fa06809f /src
parent071e07350c85a6376aa5bd3b900a59ebf6c304ee (diff)
feat: Completion for text sequence
* Type "-" or "`", autocomplet for YMAL doc separator or code fence * Delete a char of YAML doc separator or code fence, auto empty current line * Prevent more content after YAML doc separator
Diffstat (limited to 'src')
-rw-r--r--src/editor.mjs87
1 files changed, 66 insertions, 21 deletions
diff --git a/src/editor.mjs b/src/editor.mjs
index b99e34f..67df938 100644
--- a/src/editor.mjs
+++ b/src/editor.mjs
@@ -97,7 +97,7 @@ const addClassToCodeLines = () => {
97 const lines = cm.getLineHandle(0).parent.lines 97 const lines = cm.getLineHandle(0).parent.lines
98 let insideCodeBlock = false 98 let insideCodeBlock = false
99 lines.forEach((line, index) => { 99 lines.forEach((line, index) => {
100 if (line.text.match(/^````*/)) { 100 if (line.text.match(/^[\u0060]{3}/)) {
101 insideCodeBlock = !insideCodeBlock 101 insideCodeBlock = !insideCodeBlock
102 } else if (insideCodeBlock) { 102 } else if (insideCodeBlock) {
103 cm.addLineClass(index, "text", "inside-code-block") 103 cm.addLineClass(index, "text", "inside-code-block")
@@ -108,11 +108,59 @@ const addClassToCodeLines = () => {
108} 108}
109addClassToCodeLines() 109addClassToCodeLines()
110 110
111const completeForCodeBlock = (change) => {
112 const line = change.to.line
113 if (change.origin === "+input") {
114 const text = change.text[0]
115
116 // Completion for YAML doc separator
117 if (text === "-" && change.to.ch === 0 && insideCodeblockForMap(cm.getCursor())) {
118 cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 })
119 cm.replaceSelection(text.repeat(3))
120 }
121
122 // Completion for Code fence
123 if (text === "`" && change.to.ch === 0) {
124 cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 })
125 cm.replaceSelection(text.repeat(3))
126 const numberOfFences = cm.getValue()
127 .split('\n')
128 .filter(line => line.match(/[\u0060]{3}/))
129 .length
130 if (numberOfFences % 2 === 1) {
131 cm.replaceSelection('map\n\n```')
132 cm.setCursor({ line: line + 1 })
133 }
134 }
135 }
136
137 // For YAML doc separator, <hr> and code fence
138 // Auto delete to start of line
139 if (change.origin === "+delete") {
140 const match = change.removed[0].match(/^[-\u0060]$/)?.at(0)
141 if (match && cm.getLine(line) === match.repeat(2) && match) {
142 cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 2 })
143 cm.replaceSelection('')
144 }
145 }
146}
147
111// Re-render HTML by editor content 148// Re-render HTML by editor content
112cm.on("change", (_, obj) => { 149cm.on("change", (_, change) => {
113 markdown2HTML(HtmlContainer, editor.value()) 150 markdown2HTML(HtmlContainer, editor.value())
114 createDocLinks(HtmlContainer) 151 createDocLinks(HtmlContainer)
115 addClassToCodeLines() 152 addClassToCodeLines()
153 completeForCodeBlock(change)
154})
155
156cm.on("beforeChange", (_, change) => {
157 const line = change.to.line
158 // Don't allow more content after YAML doc separator
159 if (change.origin.match(/^(\+input|paste)$/)) {
160 if (cm.getLine(line) === "---" && change.text[0] !== "") {
161 change.cancel()
162 }
163 }
116}) 164})
117 165
118// Reload editor content by hash value 166// Reload editor content by hash value
@@ -153,26 +201,23 @@ fetch(defaultApply)
153 }) 201 })
154 .catch(err => console.warn(`Fail to get aliases from ${defaultApply}`, err)) 202 .catch(err => console.warn(`Fail to get aliases from ${defaultApply}`, err))
155// }}} 203// }}}
156// FUNCTION: Check cursor is inside map code block {{{
157// const insideCodeblockForMap = (currentLine) => {
158// let tokens = cm.getLineTokens(currentLine)
159//
160// if (!tokens.includes("comment") || tokens.includes('formatting-code-block')) return false
161//
162// do {
163// line = line - 1
164// if (line < 0) return false
165// tokens = cm.getLineTokens(line)
166// } while (!tokens.includes('formatting-code-block'))
167//
168// return true
169// }
170// }}}
171// FUNCTION: Check if current token is inside code block {{{ 204// FUNCTION: Check if current token is inside code block {{{
172const insideCodeblockForMap = (anchor) => { 205const insideCodeblockForMap = (anchor) => {
173 const token = cm.getTokenAt(anchor) 206 const token = cm.getTokenAt(anchor)
174 const result = token.state.overlay.codeBlock && !cm.getLine(anchor.line).match(/^````*/) 207 const insideCodeBlock = token.state.overlay.codeBlock && !cm.getLine(anchor.line).match(/^[\u0060]{3}/)
175 return result 208 if (!insideCodeBlock) return false
209
210 let line = anchor.line - 1
211 while (line >= 0) {
212 const content = cm.getLine(line)
213 if (content === '```map') {
214 return true
215 } else if (content === '```'){
216 return false
217 }
218 line = line - 1
219 }
220 return false
176} 221}
177// }}} 222// }}}
178// FUNCTION: Get Renderer by cursor position in code block {{{ 223// FUNCTION: Get Renderer by cursor position in code block {{{
@@ -191,7 +236,7 @@ const getLineWithRenderer = (anchor) => {
191 const text = cm.getLine(pl) 236 const text = cm.getLine(pl)
192 if (match(pl)) { 237 if (match(pl)) {
193 return pl 238 return pl
194 } else if (text.match(/^---|^````*/)) { 239 } else if (text.match(/^---|^[\u0060]{3}/)) {
195 break 240 break
196 } 241 }
197 pl = pl - 1 242 pl = pl - 1
@@ -202,7 +247,7 @@ const getLineWithRenderer = (anchor) => {
202 const text = cm.getLine(nl) 247 const text = cm.getLine(nl)
203 if (match(nl)) { 248 if (match(nl)) {
204 return nl 249 return nl
205 } else if (text.match(/^---|^````*/)) { 250 } else if (text.match(/^---|^[\u0060]{3}/)) {
206 return null 251 return null
207 } 252 }
208 nl = nl + 1 253 nl = nl + 1