@@ -22,7 +22,9 @@ import {
2222import {
2323 autocompletion ,
2424 closeBrackets ,
25- closeBracketsKeymap
25+ closeBracketsKeymap ,
26+ completionStatus ,
27+ selectedCompletionIndex
2628} from '@codemirror/autocomplete' ;
2729import {
2830 highlightSelectionMatches ,
@@ -258,15 +260,136 @@ function getFileEmmetConfig(fileName) {
258260 }
259261}
260262
263+ function focusOnReferenceArrow ( view ) {
264+ if ( completionStatus ( view . state ) !== 'active' ) return false ;
265+
266+ const selectedIndex = selectedCompletionIndex ( view . state ) ;
267+ if ( selectedIndex == null || selectedIndex < 0 ) return false ;
268+
269+ const tooltip = view . dom . querySelector ( '.cm-tooltip-autocomplete' ) ;
270+ if ( ! tooltip ) return false ;
271+
272+ const options = tooltip . querySelectorAll ( 'li.CodeMirror-hint' ) ;
273+ const selectedOption = options [ selectedIndex ] ;
274+ if ( ! selectedOption ) return false ;
275+
276+ const link = selectedOption . querySelector ( '.cm-completionRefLink' ) ;
277+ if ( ! link ) return false ;
278+
279+ link . focus ( ) ;
280+ link . classList . add ( 'focused-hint-link' ) ;
281+
282+ const cleanup = ( ) => {
283+ link . classList . remove ( 'focused-hint-link' ) ;
284+ link . removeEventListener ( 'blur' , cleanup ) ;
285+ } ;
286+ link . addEventListener ( 'blur' , cleanup ) ;
287+
288+ return true ;
289+ }
290+
261291// Extra custom keymaps.
262292// TODO: We need to add sublime mappings + other missing extra mappings here.
263- const extraKeymaps = [ { key : 'Tab' , run : insertTab , shift : indentLess } ] ;
293+ const extraKeymaps = [
294+ { key : 'ArrowRight' , run : focusOnReferenceArrow } ,
295+ { key : 'Tab' , run : insertTab , shift : indentLess }
296+ ] ;
264297const emmetKeymaps = [ { key : 'Tab' , run : expandAbbreviation } ] ;
265298
266299export const AUTOCOMPLETE_OPTIONS = {
267300 tooltipClass : ( ) => 'CodeMirror-hints' ,
268- optionClass : ( ) => 'CodeMirror-hint' ,
269- closeOnBlur : false
301+ closeOnBlur : false ,
302+ icons : false ,
303+
304+ // handle css classes
305+ optionClass ( completion ) {
306+ let className = 'CodeMirror-hint' ;
307+
308+ if ( completion . type ) {
309+ className += ` hint-type-${ completion . type } ` ;
310+ }
311+
312+ if ( completion . p5DocPath ) {
313+ className += ' has-doc-link' ;
314+ }
315+
316+ return className ;
317+ } ,
318+
319+ addToOptions : [
320+ {
321+ position : 60 ,
322+ render ( completion ) {
323+ const kind = document . createElement ( 'span' ) ;
324+ kind . className = 'cm-completionKind' ;
325+ kind . textContent = completion . kindLabel || completion . type || '' ;
326+ return kind ;
327+ }
328+ } ,
329+ {
330+ position : 80 ,
331+ render ( completion , state , view ) {
332+ if ( ! completion . p5DocPath ) return null ;
333+
334+ // TODO: add in reference url version switching
335+ const link = document . createElement ( 'a' ) ;
336+ link . className = 'cm-completionRefLink' ;
337+ link . href = `https://p5js.org/reference/p5/${ completion . p5DocPath } ` ;
338+ link . target = '_blank' ;
339+ link . rel = 'noopener noreferrer' ;
340+ link . tabIndex = - 1 ;
341+ link . setAttribute ( 'aria-label' , `Open ${ completion . label } reference` ) ;
342+
343+ link . innerHTML = `
344+ <span class="hint-hidden">open ${ completion . label } reference</span>
345+ <span aria-hidden="true">➔</span>
346+ ` ;
347+
348+ link . addEventListener ( 'mousedown' , ( event ) => {
349+ event . preventDefault ( ) ;
350+ event . stopPropagation ( ) ;
351+ } ) ;
352+
353+ link . addEventListener ( 'click' , ( event ) => {
354+ event . stopPropagation ( ) ;
355+ } ) ;
356+
357+ link . addEventListener ( 'keydown' , ( event ) => {
358+ if ( event . key === 'ArrowLeft' || event . key === 'Escape' ) {
359+ event . preventDefault ( ) ;
360+ event . stopPropagation ( ) ;
361+ link . classList . remove ( 'focused-hint-link' ) ;
362+ view . focus ( ) ;
363+ }
364+ } ) ;
365+
366+ return link ;
367+ }
368+ } ,
369+ {
370+ position : 100 ,
371+ render ( completion ) {
372+ if ( ! completion . blacklisted ) return null ;
373+
374+ const warning = document . createElement ( 'div' ) ;
375+ warning . className = 'cm-completionWarning' ;
376+
377+ const icon = document . createElement ( 'span' ) ;
378+ icon . className = 'cm-completionWarningIcon' ;
379+ icon . setAttribute ( 'aria-hidden' , 'true' ) ;
380+ icon . textContent = '⚠️' ;
381+
382+ const text = document . createElement ( 'span' ) ;
383+ text . className = 'cm-completionWarningText' ;
384+ text . textContent = 'use with caution in this context' ;
385+
386+ warning . appendChild ( icon ) ;
387+ warning . appendChild ( text ) ;
388+
389+ return warning ;
390+ }
391+ }
392+ ]
270393} ;
271394
272395/**
0 commit comments