-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternative codespan-reporting
style formatting?
#11
Comments
One of the major setbacks would be the need to expose the internal definitions of e.g. reports, diagnostics, etc. (mainly all that is present in However, if we allow retrieving files from a given If this is a feature that is very much wanted, then there are a few changes that will need to be done:
I am much less confident in the 2nd alternative, even thought the 1st one is actually way harder to implement. ⁽¹⁾: we are already able to retrieve |
Thanks for your reply! I am still learning my way in the code base, so I cannot comment about the proposed approaches yet. Actually, after submitting this issue, I pulled your repo down on my local Windows machine and encountered a link error, for which I believe |
I can take a look at how to structure that (and the best way to do it, although I may have an idea already) and maybe start working on it in a separate branch if you want me to. :) As for the bug with |
I have just uploaded my changes to And regarding Windows support, I have a bad news to tell: GHC's support for Unicode output through standard IO on Windows in general is still messy. I see a MR on GitLab that claims to fix it, but I cannot find the RTS switch |
Alright so there's basically nothing we can do regarding the Unicode + Windows combination, other than proposing alternative ASCII layouts (which is already the case for the default layout, although it looks less fancy that way). |
I absolutely hate the fact that everything is misaligned, and it looks like Windows is trying its best to render the unicode box characters but absolutely fails to do so with the correct width.
I am not, so this is a bit weird.
But I don't remember which version I used to generate the .cabal file (might have been stack 2.7.3 at the time). |
This is what I have, yet another peculiarity I guess:
So stack is actually shipping officially the same version 2.7.5 with different |
This shouldn't be a problem at all if your version of |
I started refactoring and moving code as per the first part of my first suggestion, in the branch Documentation has not been updated at all (there will be A LOT of places to update it), but all the rendering tests pass and give the same results as before. As of now, we should try to see which parts of the code for the ariadne layout would be better off inside the
|
I have just submitted solidsnack/wcwidth#6, but I am not very positive that
I have just started reading your code in the
Actually stack refused to regenerate the |
If the pure Haskell implementation is fully compliant with the “original” C version, I am not that bothered by including it inside this library. It'd be much better though if this could be included within a new Hackage release of the
Ah! I did not know that. You should not have to change any
I also had the idea to create a few more layouts (e.g. GCC-like) just for the sake of it, sort of to demonstrate the capabilities of this library, and perhaps give more choice regarding the output format. For now, if you need something from my code, feel free to copy-paste it, as it will make it easier to see what's in common later. :) |
In the meantime, I went ahead and implemented a GCC-style layout (and style) for the library. I'll wait a bit to push it onto the branch, as I have done some changes to how the code is organised in other modules too (such as |
I guess similar things happen in I have been fighting with stack and GHC for debugging for a while, because the stack I use was built with GHC 8.10.4 and therefore has no support for |
Unfortunately, I don't think that reusability is doable here. EDIT: here are the changes I made related to the styling:
type Style ann = IsAnnotation ann => Doc ann -> Doc AnsiStyle
-- | The class of annotation, allowing to map each to a given color style.
--
-- Every annotation used in a 'Style' must implemented this typeclass.
class IsAnnotation ann where
-- | To be used with 'reAnnotate'.
--
-- Transforms the custom annotations into color annotations as described by 'AnsiStyle'.
mkColor :: ann -> AnsiStyle
type Layout msg ann = (IsAnnotation ann, Pretty msg) => Bool -> Int -> Diagnostic msg -> Doc ann
-- | Prints a 'Diagnostic' onto a specific 'Handle'.
printDiagnostic ::
(MonadIO m, Pretty msg, IsAnnotation ann) =>
-- | The handle onto which to output the diagnostic.
Handle ->
-- | Should we print with unicode characters?
Bool ->
-- | 'False' to disable colors.
Bool ->
-- | The number of spaces each TAB character will span.
Int ->
-- | The style in which to output the diagnostic.
Style ann ->
-- | The layout to use to render the diagnostic.
Layout msg ann ->
-- | The diagnostic to output.
Diagnostic msg ->
m ()
printDiagnostic handle withUnicode withColors tabSize style layout diag =
liftIO $ hPutDoc handle ((if withColors then style else unAnnotate) $ layout withUnicode tabSize diag)
{-# INLINE printDiagnostic #-} I can push them whenever you need them.
Don't worry at all, take all the time you need!
By the way, do you think this is worth mentioning in the documentation? |
Yes, I think that would be very helpful for users of this library, especially Haskell newcomers or people who have not explicitly dealt with Unicode issues in the past. It could be in the README, the package documentation, or even a stand-alone tutorial if you wish.
In my experience, On the other hand, at least according to the tests I have done these days, the RTS option For I can elaborate on these points later if you wish to include them in the doc. Footnotes
|
This is included in the documentation, but is needed only for the FFI
I pretty much would like to include them in the docs, yes. I'm curious because I didn't know one could do that, but how did you make the little linkable footer at the end of your comment? |
I believe it is part of the extended Markdown syntax: use Here is a GitHub blog that announced this syntax is supported on GitHub. |
A quick update on |
I see. That's a bit weird but I never really took a look at the Haskell implementation of
I hope you'll be able to figure all this out. Unfortunately, I don't see the original maintainer giving help on this as they don't seem to use Haskell anymore, but I hope you could take ownership of it? Nobody else seems to be quite interested in it though... I have just discovered that the original pure Haskell implementation was based on this pure Python implementation. Maybe there's some stuff you could gather from it? (checking from the issue tab, there's only a few minor bugs, but it should work for most Unicode standard versions) |
I was also searching on this topic, and found out there once was a
Yes, I also don't expect to receive help from the original maintainer. To be fair, the original package was essentially a thin wrapper around the C function And BTW, the bug for space character is due to these line: I will take some time to also check for other bugs before resubmitting the PR. |
And regarding the width of control characters, I believe it is perfectly okay to just special case for tab, backspace, etc. and assume the width is 1 for the rest. Most terminal emulators also don't handle the control characters, and display them as solid rectangles (or whatever the "missing" glyph is in the font), which usually have width 1. |
I have to admit that I also quite like that! Definitely better.
I think that the dependency count accounts only for package within stackage snapshots. For example,
I quickly took a look at your new implementation, and I was ready to leave a comment exactly at this exact line in your code, but I simply could not understand whether your binary search was inclusive or exclusive on the last number of each range.
We already had a special case for the tab characters (so that you as the user choose how many spaces are used to render a tab character). I'm worried though that there will be many special cases for most of control characters. But if they are rendered anyway as single cell characters, then I guess it's fine to consider most of them as having a width of 1. |
For the most part, WCWidth relies on the native There is a mistake, on WCWidthHaskell.hs:415 -- @Mesabloo is correct that, per POSIX, Please find attached, the ranges pulled from the native implementation on a recent Macintosh. |
Oh hi there!
This is pretty much what @ruifengx figured in #11 (comment). I believe that a fix will be coming your way in the next few days (along with a rewrite of the pure Haskell implementation for a better/more efficient search strategy).
However, would it be ok to provide a more “Haskell-ish” way of handling |
It took me much longer than I expected, but anyway I have just finished a prototype of the
Currently the placement of multi-line markers is not ideal:
I have a plan of improvement and will rewrite this part soon. Below are some thoughts during the coding these days:
|
This already looks nice! Although is it me or do the single-line
I don't want to give a definitive answer yet, but:
I don't think code length is really an issue, unless it leads to very poor performances (which is not that huge of an issue because rendering is usually done only once in the pipeline).
No problem here.
I don't think I understood. Regarding the current output: it looks like it uses half unicode half ASCII. Do you intend it to be that way? Also, will you provide unicode-free alternatives? If not, then I think that deciding whether to provide Unicode+ASCII or just Unicode or just ASCII should be left to the implementor of the layout, hence moved outside of the core library. |
This is why I asked whether the source ranges should be closed or half-open (that is, should the larger end-point be inclusive or exclusive). I am currently assuming them to be inclusive on both ends, and I coded the rendering in such a way to allow "off-by-one" markers (pointing at technically where
AFAIK, in
I should have clarified this in the previous comment. All the screenshots are for the Unicode output (rendering style of For switching between Unicode and ASCII, I followed the approach in the original crate. Instead of passing around a |
Yeah, it looks to me the proper solution. Although there was an oversight in my last comment: note how the diagnostic produced by rustc suggested adding a data HelpMarker msg
= Add { _insertionPoint :: SrcPos, _text :: String, message :: msg }
| Remove { _removalRange :: SrcRange, message :: msg }
| Annotate { _annotationRange :: SrcRange, message :: msg } (field names given only for clarity) Or rather (factor out the common data HelpMarkerType
= Add SrcPos String
| Remove SrcRange
| Annotate SrcRange
type HelpMarker msg = (HelpMarkerType, msg) Anyway, I will first try to craft a working implementation, and the design will probably require some more tweaks to achieve the desired flexibility. Footnotes
|
This proposal will be very hard to handle, in that we have to modify the source code shown but keep the correct positions for markers. EDIT:
I can see how a public assume {@0 A : type} In the current state, the error given for this line is:
Perhaps the error message could also include a
This is not such a problem is it? We could require a
I don't think they really have a use outside of help/note sections. They could even mess up error reporting if used outside those sections, as the source code for the main markers will not be reliable (the purpose of putting the source code at the top is for it to be reliable, otherwise it is useless/confusing1). Footnotes
|
Agreed. I will spend several days to experiment on this and see if it is viable at all.
I'm not entirely sure about the
Since
Now that you mention it, yes it will be confusing. I was originally visualizing the rendering in such a way that e.g. the inserted text use fainted colours, but of course we do want the diagnostic not to cause any misunderstanding even if colours are turned off.
Yes, in this way the user gets full control over how many characters the marker should span. Though I don't expect "insertion markers" to affect the positions of other following markers, that is to say, I would naturally expect all positions used in the markers should correspond to the positions in the original (unmodified) text. I know the idea definitely needs polishing and it has to be verified in the implementation. I will come back with the result several days later, and we may have a deeper understanding then to decide on the details. |
Hello! Do you have any update? |
From #11 (comment)
I have thought about it a bit and I think that a For the other points, we already discussed them before so there's nothing more to be added here. From #11 (comment)
I can also try on my end, see what I can come up with (hopefully something that can be reused/that is generic). |
From #11 (comment)
What I thought would be pretty hard to handle turned out to be way simpler!
markers2 =
[ (Position (2, 38) (2, 47) "test1", Left $ Primary ""),
(Position (2, 5) (2, 11) "test1", Right $ AddCode "unsafe " "")
] The λ> print (fst <$> markers')
[ Position {begin = (2,45), end = (2,54), file = "test1"},
Position {begin = (2,5), end = (2,11), file = "test1"}
] Only the position of the first marker is affected, because it appears after a It also works when multiple This also works when the added piece of text contains newline, though I think this will be hard to handle when showing markers. This code is more generic than it is required to be. Instead of either a list of Here is the source code (which is rather long, so put into a dropdown section) Code for `fetchLine`fetchLine ::
forall msg ann.
Pretty msg =>
-- | The mapping between file names and file contents.
FileMap ->
-- | The path to the file to get the line from.
FilePath ->
-- | The line number corresponding to the line to get.
Int ->
-- | The number of spaces used to show a TAB character.
Int ->
-- | The annotation to add to the line when no line is found, in order to colorize the placeholder text (such as @"<no line>"@).
(ann, msg) ->
-- | The annotation to add to the line when there are no markers underneath.
ann ->
-- | The annotation to add to the line when a marker is found underneath.
(Either (SimpleMarker msg) (NoteMarker msg) -> ann) ->
-- | All the markers present on that line.
--
-- We want to apply different treatments to simple markers and marker which can appear in notes.
[(Position, Either (SimpleMarker msg) (NoteMarker msg))] ->
-- | Returns the width table and the generated annotated 'Doc'ument.
--
-- Markers are also returned with the position adjusted to fit the code perfectly (in case
-- of code modification with 'AddCode' markers).
(WidthTable, [(Position, Either (SimpleMarker msg) (NoteMarker msg))], Doc ann)
fetchLine files path line tabSize (noLineAnn, noLineText) codeAnn markerAnn markers =
case safeArrayIndex (line - 1) =<< files HashMap.!? path of
Nothing -> (mkWidthTable "", [], annotate noLineAnn (pretty noLineText))
Just code -> mkTuple3 (mkWidthTable code, colorizeCode 1 markers True code)
where
colorizeCode :: Integer -> [(Position, Either (SimpleMarker msg) (NoteMarker msg))] -> Bool -> String -> ([(Position, Either (SimpleMarker msg) (NoteMarker msg))], Doc ann)
colorizeCode _ markers _ "" = (markers, mempty)
colorizeCode n markers handleAdd (c : code)
-- if we found that a note marker starts at this position,
-- then we have to start potentially overriding the code (by adding chunks, for example).
--
-- in such case, we stop processing the code at this point, add the chunk of code
-- and then continue.
-- we also want to adjust the position
| (True, Just (AddCode code' _)) <- (handleAdd, specialNoteMarkerAt n markers) =
let addLength = length code'
-- register the length of the text, to add to all marker positions
-- starting after @n@.
-- WARN: don't partition here! we want to keep the original order of the markers.
markers' =
markers <&> \tup@(Position (bl, bc) (el, ec) f, m) ->
if bc < fromIntegral n
then tup
else case m of
-- if this is the marker we are currently handling, do NOT shift it!
Right (AddCode _ _)
| bl == line && bc == fromIntegral n -> tup
_ ->
let newStart = if bl == line then bc + addLength else bc
newEnd = if el == line then ec + addLength else ec
in (Position (bl, newStart) (el, newEnd) f, m)
in colorizeCode n markers' False (code' <> (c : code))
| otherwise =
let doc = ifTab (fold $ replicate tabSize space) pretty c
allMarkersAtPos =
snd <$> flip filter markers \case
(Position (bl, bc) (el, ec) _, _)
| bl == el ->
-- for inline markers (those which the ending line is the same as the starting line),
-- we can just check if n is included insie @[start, end[@.
fromIntegral n >= bc && fromIntegral n < ec
| otherwise ->
-- for multiline markers, it is a little bit more complicated.
(bl == line && fromIntegral n >= bc)
|| (el == line && fromIntegral n < ec)
|| (bl < line && el > line)
colorizedDoc = maybe (annotate codeAnn) (annotate . markerAnn) (head' allMarkersAtPos) doc
in second (colorizedDoc <>) $ colorizeCode (n + 1) markers True code
specialNoteMarkerAt :: Integer -> [(Position, Either (SimpleMarker msg) (NoteMarker msg))] -> Maybe (NoteMarker msg)
specialNoteMarkerAt n = fmap (fromRight undefined . snd) . find \(pos, mark) -> isRight mark && snd (begin pos) == fromIntegral n
ifTab :: a -> (Char -> a) -> Char -> a
ifTab a _ '\t' = a
ifTab _ f c = f c
mkWidthTable :: String -> WidthTable
mkWidthTable s = listArray (1, length s) (ifTab tabSize wcwidth <$> s)
head' :: [a] -> Maybe a
head' [] = Nothing
head' (x : _) = Just x
{-# INLINE head' #-}
mkTuple3 :: (a, (b, c)) -> (a, b, c)
mkTuple3 (x, (y, z)) = (x, y, z)
{-# INLINE mkTuple3 #-} If there's something which isn't quite clear, feel free to comment on it! |
Thanks for the update! I was mainly working on code clean up, because the rendering logic was previously written as two long functions, and I now managed to break one of them (the entry, for rendering Alongside code restructuring and documentations, I also implemented a basic "grouping" logic, so the following problem is now solved:
I am also currently investigating on the new marker types for attached notes and helps. This is another reason why I was refactoring the code. I believe the rendering could reuse a substantial amount of code (I mean the part currently responsible for rendering the sub-reports for each file, namely |
About attached notes and helps, I have changed the For now, the code looks like this (in -- | A 'HashMap' associating a 'FilePath' to an array of lines indexed by line numbers.
type FileMap = HashMap FilePath (Array Int String)
-- | The type of diagnostic reports with abstract message type.
data Report msg
= Report
Severity
-- ^ The severity of the report.
(Maybe msg)
-- ^ An optional error code to print with the report.
msg
-- ^ The message associated with the report.
[(Position, SimpleMarker msg)]
-- ^ A map associating positions to markers to show under the source code.
[Note msg]
-- ^ A list of notes and hints to show various help information after the error.
-- | The severity of a report describes how much it is important to take into account.
data Severity
= -- | A warning report, which is important but not necessary to fix.
Warning
| -- | An error report, which stops the whole pipeline.
Error
| -- | A critical report, indicating that something internally failed.
Critical
-- | The type of markers which are meant to be shown under a line of code.
data SimpleMarker msg
= -- | A primary marker, which conveys the most important information (such as the location of an error).
Primary msg
| -- | A secondary marker, which is meant to add extra information to the report.
Secondary msg
| -- | A blank marker which is invisible but allows to include specific lines in the final report.
Blank
#ifdef USE_AESON
instance ToJSON Severity where
toJSON Warning = toJSON ("warning" :: String)
toJSON Error = toJSON ("error" :: String)
toJSON Critical = toJSON ("critical" :: String)
instance ToJSON msg => ToJSON (Report msg) where
toJSON (Report sev code msg markers hints) =
object [ "kind" .= sev
, "code" .= code
, "message" .= msg
, "markers" .= fmap showMarker markers
, "notes" .= hints
]
where
showMarker (pos, marker) =
object $ [ "position" .= pos ]
<> case marker of
Primary m -> [ "message" .= m, "kind" .= ("primary" :: String) ]
Secondary m -> [ "message" .= m, "kind" .= ("secondary" :: String) ]
Blank -> [ "kind" .= ("blank" :: String) ]
#endif
-- | A note is a piece of information that is found at the end of a report.
data Note msg
= -- | A note, which is meant to give valuable information related to the encountered error.
Note msg [(Position, NoteMarker msg)]
| -- | A hint, to propose potential fixes or help towards fixing the issue.
Hint msg [(Position, NoteMarker msg)]
data NoteMarker msg
= -- | We need a piece of code to be added inside the source code.
AddCode
String
-- ^ The piece of code to insert. This must NOT contain newlines.
-- This may also be longer or shorter than the span of the marker.
msg
-- ^ The possibly empty message attached to the marker.
| -- | Code is requested to be deleted because it causes an error.
RemoveCode
msg
-- ^ The message attached to the marker.
| -- | A simple annotation under source code to add extra information (e.g. in notes)
Annotate
msg
-- ^ The message attached to this marker.
#ifdef USE_AESON
instance ToJSON msg => ToJSON (Note msg) where
toJSON = \case
Note m marks -> object [ "kind" .= ("note" :: String), "message" .= m, "markers" .= fmap showMarker marks ]
Hint m marks -> object [ "kind" .= ("hint" :: String), "message" .= m, "markers" .= fmap showMarker marks ]
where
showMarker (pos, mark) = object $ [ "position" .= pos ]
<> case mark of
AddCode code m -> [ "kind" .= ("insert" :: String), "message" .= m, "code" .= code ]
RemoveCode m -> [ "kind" .= ("delete" :: String), "message" .= m ]
Annotate m -> [ "kind" .= ("annotate" :: String), "message" .= m ]
#endif
-- | Transforms a warning report into an error report.
warningToError :: Report msg -> Report msg
warningToError (Report Warning code msg markers notes) = Report Error code msg markers notes
warningToError r@(Report _ _ _ _ _) = r
-- | Transforms an error report into a warning report.
errorToWarning :: Report msg -> Report msg
errorToWarning (Report Error code msg markers notes) = Report Warning code msg markers notes
errorToWarning r@(Report _ _ _ _ _) = r Let me know what you think about it! :) All the changes are summarized below:
I chose not to include full reports in notes following what we discussed earlier.
Great! This looks way better. |
I copied the code you posted here in The code and the updated test case is available in my fork. I think there are still edge cases that I does not currently handle properly, so there will probably still be some updates, most likely alongside a refactor of P.S. I find the current interface for |
The current interface for
This would also work. When the In my opinion, it would be better to disallow these, and perform character substitution when adding code (as described in 2. above), but I would like to have your opinion too. (which I believe I can infer as the same thing, given how you defined the
I like it! It's almost as if I can't recognize that this is actually Diagnose rendering these. :D
I'll most probably make a very big commit here in some time, after finishing updating the original ariadne layout to support markers in notes. |
Just a random thought: we now have markers which can signify adding or removing code (where the former actually adds code). Do we also want to add markers to edit the source code (i.e. change a range to some code)? |
I am not against this idea, but given the current types of markers, I would expresses this intent using the
By the way, would you mind if I introduce |
As long as it is not part of the core library, then go ahead! The idea will be to release alternative layouts as separate packages, so more dependencies for a layout package isn't a problem.
I see. I wasn't quite sure whether messages were useful on note markers, so I actually removed them (temporarily, I guess) in my own code. But seeing this, I'll make sure to add them back (at least for |
Alright so I have pushed all I had on the Feel free to give any feedback! It will be greatly appreciated. |
Also, I randomly thought about it the other day, but given that all layouts will be released under different packages, perhaps there is no need to include yours inside this repository? |
Personally, I prefer merging my code into your repo so that it is easier to find and less likely to go out of sync. But certainly I am happy to maintain this part of code in case future changes make maintenance a non-trivial task. I have just merged your branch into my fork (well technically it was a rebase), and submitted #14. The current version does not support multiline Comments for your implementation: in my understanding, the following code probably does not work as intended, because this is not an orphan instance, so even if the users write their own implementation, there is no way that instance resolution could prefer the user-defined ones (it won't be more specific than the version here). The only possibility I know is
|
I ended up removing that part of the logic. Instead, I am replacing any vertical whitespace with a Unicode symbol (see in
I believe that if the user defines a new instance as PS: I'll review your PR a bit later. :) |
If my understanding of the GHC manual is correct, resolving the instance also requires the priviledged instance to be strictly more specific. For this case, my understanding is that the two instances are equally specific. I believe we actually need the default instance in the library to be marked And regarding the review for the PR, please take your time. Also, I will be happy to address any readability/efficiency issues or code duplication. |
It turns out that you are definitely right, and that my understanding of overlapping instances was just off. EDIT: and indeed, from the GHC manual you linked, the two conditions must hold:
MWE:
And the error:
|
So I've noticed that you're hard-coding a layout style as something which can be converted to an Further, |
For example, rather than something analogous to |
I'm not quite sure that I quite follow the suggestion here. |
Sure, I'll elaborate in the morning! |
In the current approach (both 2.4.0 and the branch), there are two separate operations that are combined into one:
These two things are both conceptually and semantically distinct; a given layout can have many styles, and a given style can apply to many layouts. As an example, say a user really likes the Ariadne layout, but hates the colours; then they could apply a different style to the Ariadne layout. So, the idea is two have two passes: layout, then styling. The user should get to choose when to apply styling; and they should get to choose what style to apply to a given layout. data DiagnosticAnnotation = File FilePath
| SourceLocation Position
| Severity SeverityLevel
| DiagnosticCode
| ...
-- | A prismatic class for diagnostic annotations, as they may be embedded
-- into a larger annotation type in a larger document.
class HasDiagnosticAnnotation diagAnn where
injectDiagnosticAnnotation :: DiagnosticAnnotation -> diagAnn
extractDiagnosticAnnotation :: diagAnn -> Maybe DiagnosticAnnotation
-- | Layout the diagnostic in a given format (ariadne, gcc, codespan-reporting, etc)
layoutDiagnostic :: HasDiagnosticAnnotation diagAnn => Diagnostic -> Doc diagAnn
-- | User may wish to colourise the diagnostics in a larger document that
-- still has other semantic annotations.
class HasAnsiStyling ann where
injectStylingAnnotation :: AnsiStyle -> ann
-- | Apply bold/underlining/italic/colour/etc
renderAsANSI :: (HasDiagnosticAnnotation diagAnn, HasAnsiStyling stylingAnn) =>
Doc diagAnn ->
Doc stylingAnn |
Reusing styles across multiple layouts is something that I would have initially liked to have.
I'd be happy to have restyling and reusing styles for multiple different layouts, but sadly I don't know if this can be done while remaining semantically correct (and avoiding dropping cases with |
Thanks for your great library!
I use both Rust and Haskell, and have learned about error reporting libraries like
ariadne
andcodespan-reporting
for a while. Previously when working on a Rust project, I chosecodespan-reporting
because I preferred its formatting (formatting ofariadne
is also beautiful, but a bit too fancy for me), given their interfaces are pretty similar. Is it possible to extend this library to have an alternativecodespan-reporting
-style formatting for the diagnostic messages? Or is there some design space to expose an API for custom report formatting?FYI, here is a comparison of the said two styles (BTW it is amusing to see a more Haskell-like syntax for illustration in
codespan-reporting
the Rust library but a more Rust-like style here):ariadne
-stylecodespan-reporting
-styleIf this is deemed non-trivial, I am willing to work on this, in that case would you please provide some guidance?
The text was updated successfully, but these errors were encountered: