@@ -14,30 +14,13 @@ import {
1414} from '@codemirror/view' ;
1515import {
1616 foldGutter ,
17- foldKeymap ,
1817 bracketMatching ,
1918 indentOnInput ,
2019 syntaxHighlighting
2120} from '@codemirror/language' ;
22- import {
23- autocompletion ,
24- closeBrackets ,
25- closeBracketsKeymap ,
26- completionStatus ,
27- selectedCompletionIndex
28- } from '@codemirror/autocomplete' ;
29- import {
30- highlightSelectionMatches ,
31- search ,
32- searchKeymap
33- } from '@codemirror/search' ;
34- import {
35- defaultKeymap ,
36- history ,
37- historyKeymap ,
38- insertTab ,
39- indentLess
40- } from '@codemirror/commands' ;
21+ import { autocompletion , closeBrackets } from '@codemirror/autocomplete' ;
22+ import { highlightSelectionMatches , search } from '@codemirror/search' ;
23+ import { history } from '@codemirror/commands' ;
4124import { lintGutter , linter } from '@codemirror/lint' ;
4225import {
4326 expandAbbreviation ,
@@ -51,16 +34,20 @@ import { xml } from '@codemirror/lang-xml';
5134import { emmetConfig } from '@emmetio/codemirror6-plugin' ;
5235import { color as colorPicker } from '@connieye/codemirror-color-picker' ;
5336
54- import { tidyCodeWithPrettier } from './tidier' ;
55- import { p5JavaScript } from './p5JavaScript' ;
56- import { highlightStyle } from './highlightStyle' ;
57- import { errorDecorationStateField } from './consoleErrorDecoration' ;
37+ import { p5JavaScript } from './utils/p5JavaScript' ;
38+ import { highlightStyle } from './utils/highlightStyle' ;
39+ import { errorDecorationStateField } from './utils/consoleErrorDecoration' ;
5840import {
5941 makeCssLinter ,
6042 makeHtmlLinter ,
6143 makeJsonLinter ,
6244 makeJavascriptLinter
63- } from './linters' ;
45+ } from './utils/linters' ;
46+ import { emmetKeymaps , buildKeymaps } from './utils/keymaps' ;
47+ import {
48+ createAutocompleteOptions ,
49+ createFoldMarker
50+ } from './utils/extensionCustomStyles' ;
6451
6552// ----- TODOS -----
6653// - shader syntax highlighting
@@ -137,179 +124,6 @@ function getFileEmmetConfig(fileName) {
137124 }
138125}
139126
140- function getColorPickerAtSelection ( view ) {
141- const { head } = view . state . selection . main ;
142- const { node } = view . domAtPos ( head ) ;
143-
144- const startEl =
145- node . nodeType === Node . ELEMENT_NODE ? node : node . parentElement ;
146-
147- const lineEl = startEl ?. closest ( '.cm-line' ) ;
148-
149- return (
150- lineEl ?. querySelector ( 'input[type="color"]:not(:disabled)' ) ||
151- view . contentDOM . querySelector ( 'input[type="color"]:not(:disabled)' )
152- ) ;
153- }
154-
155- function openColorPickerWithKeyboard ( view ) {
156- const picker = getColorPickerAtSelection ( view ) ;
157-
158- if ( ! picker || picker . disabled ) {
159- return false ;
160- }
161-
162- picker . focus ( ) ;
163-
164- if ( typeof picker . showPicker === 'function' ) {
165- picker . showPicker ( ) ;
166- } else {
167- picker . click ( ) ;
168- }
169- return true ;
170- }
171-
172- function focusOnReferenceArrow ( view ) {
173- if ( completionStatus ( view . state ) !== 'active' ) return false ;
174-
175- const selectedIndex = selectedCompletionIndex ( view . state ) ;
176- if ( selectedIndex == null || selectedIndex < 0 ) return false ;
177-
178- const tooltip = view . dom . querySelector ( '.cm-tooltip-autocomplete' ) ;
179- if ( ! tooltip ) return false ;
180-
181- const options = tooltip . querySelectorAll ( 'li.CodeMirror-hint' ) ;
182- const selectedOption = options [ selectedIndex ] ;
183- if ( ! selectedOption ) return false ;
184-
185- const link = selectedOption . querySelector ( '.cm-completionRefLink' ) ;
186- if ( ! link ) return false ;
187-
188- link . focus ( ) ;
189- link . classList . add ( 'focused-hint-link' ) ;
190-
191- const cleanup = ( ) => {
192- link . classList . remove ( 'focused-hint-link' ) ;
193- link . removeEventListener ( 'blur' , cleanup ) ;
194- } ;
195- link . addEventListener ( 'blur' , cleanup ) ;
196-
197- return true ;
198- }
199-
200- // Extra custom keymaps.
201- // TODO: We need to add sublime mappings + other missing extra mappings here.
202- const extraKeymaps = [
203- { key : 'ArrowRight' , run : focusOnReferenceArrow } ,
204- { key : 'Tab' , run : insertTab , shift : indentLess }
205- ] ;
206- const emmetKeymaps = [ { key : 'Tab' , run : expandAbbreviation } ] ;
207-
208- /** Returns completion options configured for autocomplete. */
209- export const createAutocompleteOptions = ( referenceBaseUrl ) => ( {
210- selectOnOpen : false ,
211- tooltipClass : ( ) => 'CodeMirror-hints' ,
212- closeOnBlur : false ,
213- icons : false ,
214-
215- // handle css classes
216- optionClass ( completion ) {
217- let className = 'CodeMirror-hint' ;
218-
219- if ( completion . type ) {
220- className += ` hint-type-${ completion . type } ` ;
221- }
222-
223- if ( completion . p5DocPath ) {
224- className += ' has-doc-link' ;
225- }
226-
227- return className ;
228- } ,
229-
230- addToOptions : [
231- {
232- position : 60 ,
233- render ( completion ) {
234- const kind = document . createElement ( 'span' ) ;
235- kind . className = 'cm-completionKind' ;
236- kind . textContent = completion . kindLabel || completion . type || '' ;
237- return kind ;
238- }
239- } ,
240- {
241- position : 80 ,
242- render ( completion , state , view ) {
243- if ( ! completion . p5DocPath ) return null ;
244-
245- const link = document . createElement ( 'a' ) ;
246- link . className = 'cm-completionRefLink' ;
247- link . href = `${ referenceBaseUrl } /reference/p5/${ completion . p5DocPath } ` ;
248- link . target = '_blank' ;
249- link . rel = 'noopener noreferrer' ;
250- link . tabIndex = - 1 ;
251- link . setAttribute ( 'aria-label' , `Open ${ completion . label } reference` ) ;
252-
253- link . innerHTML = `
254- <span class="hint-hidden">open ${ completion . label } reference</span>
255- <span aria-hidden="true">➔</span>
256- ` ;
257-
258- link . addEventListener ( 'mousedown' , ( event ) => {
259- event . preventDefault ( ) ;
260- event . stopPropagation ( ) ;
261- } ) ;
262-
263- link . addEventListener ( 'click' , ( event ) => {
264- event . stopPropagation ( ) ;
265- } ) ;
266-
267- link . addEventListener ( 'keydown' , ( event ) => {
268- if ( event . key === 'ArrowLeft' || event . key === 'Escape' ) {
269- event . preventDefault ( ) ;
270- event . stopPropagation ( ) ;
271- link . classList . remove ( 'focused-hint-link' ) ;
272- view . focus ( ) ;
273- }
274- } ) ;
275-
276- return link ;
277- }
278- } ,
279- {
280- position : 100 ,
281- render ( completion ) {
282- if ( ! completion . blacklisted ) return null ;
283-
284- const warning = document . createElement ( 'div' ) ;
285- warning . className = 'cm-completionWarning' ;
286-
287- const icon = document . createElement ( 'span' ) ;
288- icon . className = 'cm-completionWarningIcon' ;
289- icon . setAttribute ( 'aria-hidden' , 'true' ) ;
290- icon . textContent = '⚠️' ;
291-
292- const text = document . createElement ( 'span' ) ;
293- text . className = 'cm-completionWarningText' ;
294- text . textContent = 'use with caution in this context' ;
295-
296- warning . appendChild ( icon ) ;
297- warning . appendChild ( text ) ;
298-
299- return warning ;
300- }
301- }
302- ]
303- } ) ;
304-
305- // Uses window.document explicitly to avoid shadowing by the `document`
306- // parameter in createNewFileState below.
307- function createFoldMarker ( open ) {
308- const span = window . document . createElement ( 'span' ) ;
309- span . className = open ? 'cm-fold-open' : 'cm-fold-closed' ;
310- return span ;
311- }
312-
313127/**
314128 * Creates a new CodeMirror editor state with configurations,
315129 * extensions, and keymaps tailored to the file type and settings.
@@ -338,43 +152,7 @@ export function createNewFileState(filename, document, settings) {
338152 // across files via a shared module-level array.
339153 const mode = getFileMode ( filename ) ;
340154
341- const colorPickerKeymap = [ ] ;
342- if ( mode === 'css' || mode === 'javascript' ) {
343- colorPickerKeymap . push ( {
344- key : 'Mod-k' ,
345- run : ( view ) => openColorPickerWithKeyboard ( view )
346- } ) ;
347- }
348-
349- // Make a keymap for both uppercase and lowercase F, since
350- // since browsers can differ in which one they send for the Shift-Mod-F shortcut.
351- const fileTidyKeymap = [
352- {
353- key : 'Shift-Mod-F' ,
354- run : ( cmView ) => {
355- tidyCodeWithPrettier ( cmView , mode ) ;
356- return true ;
357- }
358- } ,
359- {
360- key : 'Shift-Mod-f' ,
361- run : ( cmView ) => {
362- tidyCodeWithPrettier ( cmView , mode ) ;
363- return true ;
364- }
365- }
366- ] ;
367-
368- const keymaps = [
369- extraKeymaps ,
370- colorPickerKeymap ,
371- fileTidyKeymap ,
372- closeBracketsKeymap ,
373- defaultKeymap ,
374- historyKeymap ,
375- foldKeymap ,
376- searchKeymap
377- ] ;
155+ const keymaps = buildKeymaps ( mode ) ;
378156
379157 // https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
380158 const extensions = [
0 commit comments