11import { useState , useEffect , useLayoutEffect , useRef } from "react"
22import * as monaco from "monaco-editor"
3- import { LintError , useDumpCst , useDumpTokens , useErrors } from "./squawk"
3+ import { LintError , Fix , useDumpCst , useDumpTokens , useErrors } from "./squawk"
44import {
55 compress ,
66 compressToEncodedURIComponent ,
@@ -231,14 +231,16 @@ function Editor({
231231 onChange ?: ( _ : string ) => void
232232 onSave ?: ( _ : string ) => void
233233 settings : monaco . editor . IStandaloneEditorConstructionOptions
234- markers ?: monaco . editor . IMarkerData [ ]
234+ markers ?: Marker [ ]
235235} ) {
236236 const onChangeRef = useRef < ( ( _ : string ) => void ) | undefined > ( null )
237237 const onSaveRef = useRef < ( ( _ : string ) => void ) | undefined > ( null )
238238 const divRef = useRef < HTMLDivElement > ( null )
239239 const autoFocusRef = useRef ( autoFocus )
240240 const settingsInitial = useRef ( settings )
241241 const editorRef = useRef < monaco . editor . IStandaloneCodeEditor > ( null )
242+ const fixesRef = useRef < Map < string , Fix > > ( new Map ( ) )
243+
242244 // TODO: replace with useEventEffect
243245 useEffect ( ( ) => {
244246 onChangeRef . current = onChange
@@ -251,6 +253,16 @@ function Editor({
251253 if ( markers == null ) {
252254 return
253255 }
256+
257+ const fixesMap = new Map < string , Fix > ( )
258+ for ( const marker of markers ) {
259+ if ( marker . fix ) {
260+ const key = createMarkerKey ( marker )
261+ fixesMap . set ( key , marker . fix )
262+ }
263+ }
264+ fixesRef . current = fixesMap
265+
254266 const model = editorRef . current ?. getModel ( )
255267 if ( model != null ) {
256268 monaco . editor . setModelMarkers ( model , "squawk" , markers )
@@ -269,7 +281,7 @@ function Editor({
269281 onSaveRef . current ?.( editor . getValue ( ) )
270282 } )
271283 monaco . languages . register ( { id : "rast" } )
272- monaco . languages . setMonarchTokensProvider ( "rast" , {
284+ const tokenProvider = monaco . languages . setMonarchTokensProvider ( "rast" , {
273285 tokenizer : {
274286 // via: https://github.com/rust-lang/rust-analyzer/blob/9691da7707ea7c50922fe1647b1c2af47934b9fa/editors/code/ra_syntax_tree.tmGrammar.json#L16C17-L16C17
275287 root : [
@@ -284,6 +296,55 @@ function Editor({
284296 ] ,
285297 } ,
286298 } )
299+
300+ const codeActionProvider = monaco . languages . registerCodeActionProvider (
301+ "pgsql" ,
302+ {
303+ provideCodeActions : ( model , _range , context ) => {
304+ const actions : monaco . languages . CodeAction [ ] = [ ]
305+ for ( const marker of context . markers ) {
306+ if ( marker . source === "squawk" ) {
307+ const key = createMarkerKey ( marker )
308+ const fix = fixesRef . current . get ( key )
309+ if ( fix ) {
310+ const edits = fix . edits . map (
311+ ( edit ) : monaco . languages . IWorkspaceTextEdit => {
312+ return {
313+ resource : model . uri ,
314+ versionId : model . getVersionId ( ) ,
315+ textEdit : {
316+ range : new monaco . Range (
317+ edit . start_line_number + 1 ,
318+ edit . start_column + 1 ,
319+ edit . end_line_number + 1 ,
320+ edit . end_column + 1 ,
321+ ) ,
322+ text : edit . text ,
323+ } ,
324+ }
325+ } ,
326+ )
327+ actions . push ( {
328+ title : fix . title ,
329+ diagnostics : [ marker ] ,
330+ kind : "quickfix" ,
331+ edit : {
332+ edits,
333+ } ,
334+ isPreferred : true ,
335+ } )
336+ }
337+ }
338+ }
339+
340+ return {
341+ actions,
342+ dispose : ( ) => { } ,
343+ }
344+ } ,
345+ } ,
346+ )
347+
287348 editor . onDidChangeModelContent ( ( ) => {
288349 onChangeRef . current ?.( editor . getValue ( ) )
289350 } )
@@ -293,7 +354,9 @@ function Editor({
293354 editorRef . current = editor
294355 return ( ) => {
295356 editorRef . current = null
357+ codeActionProvider . dispose ( )
296358 editor ?. dispose ( )
359+ tokenProvider . dispose ( )
297360 }
298361 } , [ ] )
299362 useEffect ( ( ) => {
@@ -330,6 +393,18 @@ type Marker = monaco.editor.IMarkerData & {
330393 range_start : number
331394 range_end : number
332395 messages : string [ ]
396+ fix ?: Fix
397+ }
398+
399+ function createMarkerKey ( marker : {
400+ startLineNumber : number
401+ startColumn : number
402+ endLineNumber : number
403+ endColumn : number
404+ message : string
405+ } ) : string {
406+ // TODO: probably a better way to do this
407+ return `${ marker . startLineNumber } :${ marker . startColumn } :${ marker . endLineNumber } :${ marker . endColumn } :${ marker . message } `
333408}
334409
335410function SyntaxTreePanel ( { text } : { text : string } ) {
@@ -372,6 +447,7 @@ function useMarkers(text: string): Array<Marker> {
372447 range_start : x . range_start ,
373448 range_end : x . range_end ,
374449 messages : x . messages ,
450+ fix : x . fix ,
375451 code : {
376452 value : x . code ,
377453 target : monaco . Uri . parse (
0 commit comments