@@ -25,6 +25,13 @@ module KeyMap = {
2525 }
2626}
2727
28+ module Side = {
29+ @@warning ("-37" )
30+ type t =
31+ | @as (- 1 ) BeforePointer
32+ | @as (1 ) AfterPointer
33+ }
34+
2835module Error = {
2936 type kind = [#Error | #Warning ]
3037
@@ -66,14 +73,21 @@ module CM6 = {
6673 external fromArray : array <t > => t = "%identity"
6774 }
6875
76+ module Line = {
77+ type t = {
78+ from : int ,
79+ @as ("to" ) to_ : int ,
80+ number : int ,
81+ text : string ,
82+ length : int ,
83+ }
84+ }
85+
6986 module Text = {
70- type t
71- type line
87+ type t = {lines : int }
7288 @send external toString : t => string = "toString"
73- @get external lines : t => int = "lines"
74- @send external line : (t , int ) => line = "line"
75- @get external lineFrom : line => int = "from"
76- @get external lineLength : line => int = "length"
89+ @send external line : (t , int ) => Line .t = "line"
90+ @send external lineAt : (t , int ) => Line .t = "lineAt"
7791 }
7892
7993 module EditorSelection = {
@@ -108,6 +122,21 @@ module CM6 = {
108122 }
109123
110124 module EditorView = {
125+ module Tooltip = {
126+ module View = {
127+ type t = {dom : WebAPI .DOMAPI .element , offset ?: {x : int , y : int }}
128+ }
129+ type t = {
130+ pos : int ,
131+ end ?: int ,
132+ create : editorView => View .t ,
133+ above ?: bool ,
134+ strictSide ?: bool ,
135+ arrow ?: bool ,
136+ clip ?: bool ,
137+ }
138+ }
139+
111140 type createConfig = {state : editorState , parent : WebAPI .DOMAPI .element }
112141 @module ("@codemirror/view" ) @new
113142 external create : createConfig => editorView = "EditorView"
@@ -146,6 +175,10 @@ module CM6 = {
146175 @module ("@codemirror/view" )
147176 external dropCursor : unit => extension = "dropCursor"
148177
178+ @module ("@codemirror/view" )
179+ external hoverTooltip : ((editorView , int , Side .t ) => null <Tooltip .t >) => extension =
180+ "hoverTooltip"
181+
149182 module UpdateListener = {
150183 type update
151184 @get external view : update => editorView = "view"
@@ -449,10 +482,16 @@ module CM6 = {
449482 }
450483
451484 module Lint = {
485+ type severity =
486+ | @as ("error" ) Error
487+ // | @as("hint") Hint
488+ // | @as("info") Info
489+ | @as ("warning" ) Warning
490+
452491 type diagnostic = {
453492 from : int ,
454493 to : int ,
455- severity : string ,
494+ severity : severity ,
456495 message : string ,
457496 }
458497
@@ -487,6 +526,7 @@ type editorInstance = {
487526 readOnlyConf : CM6 .compartment ,
488527 keymapConf : CM6 .compartment ,
489528 lintConf : CM6 .compartment ,
529+ hintConf : CM6 .compartment ,
490530}
491531
492532type editorConfig = {
@@ -514,21 +554,24 @@ let createLinterExtension = (errors: array<Error.t>): CM6.extension => {
514554
515555 Array .forEach (errors , err => {
516556 try {
517- // Error row/endRow are 1-based (same as CodeMirror 5 )
518- // Error column/endColumn are 0-based (same as CodeMirror 5 )
519- let fromLine = Math .Int .max (1 , Math .Int .min (err .row , CM6 . Text . lines ( doc ) ))
520- let toLine = Math .Int .max (1 , Math .Int .min (err .endRow , CM6 . Text . lines ( doc ) ))
557+ // Error row/endRow are 1-based (same as CodeMirror 6 )
558+ // Error column/endColumn are 0-based (same as CodeMirror 6 )
559+ let fromLine = Math .Int .max (1 , Math .Int .min (err .row , doc . lines ))
560+ let toLine = Math .Int .max (1 , Math .Int .min (err .endRow , doc . lines ))
521561
522562 let startLine = CM6 .Text .line (doc , fromLine )
523563 let endLine = CM6 .Text .line (doc , toLine )
524564
525- let fromCol = Math .Int .max (0 , Math .Int .min (err .column , CM6 . Text . lineLength ( startLine ) ))
526- let toCol = Math .Int .max (0 , Math .Int .min (err .endColumn , CM6 . Text . lineLength ( endLine ) ))
565+ let fromCol = Math .Int .max (0 , Math .Int .min (err .column , startLine . length ))
566+ let toCol = Math .Int .max (0 , Math .Int .min (err .endColumn , endLine . length ))
527567
528568 let diagnostic = {
529- CM6 .Lint .from : CM6 .Text .lineFrom (startLine ) + fromCol ,
530- to : CM6 .Text .lineFrom (endLine ) + toCol ,
531- severity : err .kind === #Error ? "error" : "warning" ,
569+ CM6 .Lint .from : startLine .from + fromCol ,
570+ to : endLine .from + toCol ,
571+ severity : switch err .kind {
572+ | #Error => Error
573+ | #Warning => Warning
574+ },
532575 message : err .text ,
533576 }
534577
@@ -545,6 +588,32 @@ let createLinterExtension = (errors: array<Error.t>): CM6.extension => {
545588 CM6 .Lint .linter (linterSource )
546589}
547590
591+ let createHoverHintExtension = (hoverHints : array <HoverHint .t >) => {
592+ CM6 .EditorView .hoverTooltip ((view , pos , _side ) => {
593+ let doc = view -> CM6 .EditorView .state -> CM6 .EditorState .doc
594+ let {number : line , from } = doc -> CM6 .Text .lineAt (pos )
595+ let col = pos - from
596+ let found = hoverHints -> Array .find (({start , end }) => {
597+ line >= start .line && line <= end .line && col >= start .col && col <= end .col
598+ })
599+ switch found {
600+ | Some ({hint , start , end }) =>
601+ let pos = CM6 .Text .line (doc , start .line ).from + start .col
602+ let end = CM6 .Text .line (doc , end .line ).from + end .col
603+ let dom = WebAPI .Global .document -> WebAPI .Document .createElement ("div" )
604+ dom .textContent = Value (hint )
605+ dom .className = "p-1 border"
606+ Value ({
607+ pos ,
608+ end ,
609+ above : true ,
610+ create : _view => {dom : dom },
611+ })
612+ | None => Null
613+ }
614+ })
615+ }
616+
548617module ReScript = {
549618 @module ("@tsnobip/rescript-lezer" )
550619 external parser : CM6 .Language .LRParser .t = "parser"
@@ -589,6 +658,7 @@ let createEditor = (config: editorConfig): editorInstance => {
589658 let readOnlyConf = CM6 .Compartment .create ()
590659 let keymapConf = CM6 .Compartment .create ()
591660 let lintConf = CM6 .Compartment .create ()
661+ let hintConf = CM6 .Compartment .create ()
592662
593663 // Basic extensions
594664 let extensions = [
@@ -651,6 +721,10 @@ let createEditor = (config: editorConfig): editorInstance => {
651721
652722 // Add linter for errors (wrap the raw linter extension in the compartment)
653723 Array .push (extensions , CM6 .Compartment .make (lintConf , createLinterExtension (config .errors )))
724+ Array .push (
725+ extensions ,
726+ CM6 .Compartment .make (hintConf , createHoverHintExtension (config .hoverHints )),
727+ )
654728 Array .push (extensions , CM6 .Lint .lintGutter ())
655729
656730 // Create editor
@@ -677,6 +751,7 @@ let createEditor = (config: editorConfig): editorInstance => {
677751 readOnlyConf ,
678752 keymapConf ,
679753 lintConf ,
754+ hintConf ,
680755 }
681756}
682757
@@ -798,3 +873,12 @@ let editorSetErrors = (instance: editorInstance, errors: array<Error.t>): unit =
798873 },
799874 )
800875}
876+
877+ let editorSetHoverHints = (instance : editorInstance , hints : array <HoverHint .t >): unit => {
878+ CM6 .EditorView .dispatchEffects (
879+ instance .view ,
880+ {
881+ effects : CM6 .Compartment .reconfigure (instance .hintConf , createHoverHintExtension (hints )),
882+ },
883+ )
884+ }
0 commit comments