-
Notifications
You must be signed in to change notification settings - Fork 5
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
Add preliminary support for Mapped Types #122
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for start the work.
I clean up a little the code, made it more type safe but I think overall it looks fine.
I will also have a look to improve the error message because now with the change introduced in this PR, they are not all on par..
If you're happy with my changes to the error reporting I'll go through and add |
I added |
My way of doing it was by copy / searching the context error across the file. But having the name of the source file, is either because in general editors allow you to fuzzy search by filename in general. It is just a shame that we can't do something like: module Log =
let inline info (message: string) =
printfn $"%s{__SOURCE_FILE__}: %s{message}" Where |
d9622d8
to
0e50033
Compare
…r to read [converter]
0e50033
to
a5abdc3
Compare
let private readTypeQuery | ||
(reader: ITypeScriptReader) | ||
(typeNodeQuery: Ts.TypeQueryNode) | ||
= | ||
let checker = reader.checker | ||
let typ = checker.getTypeAtLocation !!typeNodeQuery.exprName | ||
|
||
resultOption { | ||
// This is safe as both cases have a `kind` field | ||
let exprNameKind: Ts.SyntaxKind = typeNodeQuery.exprName?kind | ||
// This logic is highly overlapping with readTypeUsingFlags | ||
// It should be refactored to achieve a sane flow | ||
match typ.flags, typ.getSymbol (), exprNameKind with | ||
| HasTypeFlags Ts.TypeFlags.Object, None, Ts.SyntaxKind.Identifier -> | ||
let exprName: Ts.Identifier = !!typeNodeQuery.exprName | ||
|
||
let! aliasSymbol = | ||
checker.getSymbolAtLocation exprName | ||
|> ResultOption.ofOption | ||
|> Result.mapError (fun _ -> | ||
generateReaderError | ||
"type node (TypeQuery)" | ||
"Missing symbol" | ||
typeNodeQuery | ||
) | ||
|
||
let! declaration = | ||
aliasSymbol.declarations | ||
|> Option.map (fun declarations -> | ||
if declarations.Count <> 1 then | ||
None | ||
else | ||
Some declarations.[0] | ||
) | ||
|> ResultOption.ofOption | ||
|> Result.mapError (fun _ -> | ||
generateReaderError | ||
"type node (TypeQuery)" | ||
"Expected exactly one declaration" | ||
typeNodeQuery | ||
) | ||
|
||
let! variableDeclaration = | ||
declaration | ||
|> Option.bind (fun declaration -> | ||
match declaration.kind with | ||
| Ts.SyntaxKind.VariableDeclaration -> | ||
Some(declaration :?> Ts.VariableDeclaration) | ||
| _ -> None | ||
) | ||
|> ResultOption.ofOption | ||
|> Result.mapError (fun _ -> | ||
generateReaderError | ||
"type node (TypeQuery)" | ||
"Unsupported declaration kind" | ||
typeNodeQuery | ||
) | ||
|
||
let! typeNode = | ||
variableDeclaration.``type`` | ||
|> ResultOption.ofOption | ||
|> Result.mapError (fun _ -> | ||
generateReaderError | ||
"type node (TypeQuery)" | ||
"Missing type" | ||
typeNodeQuery | ||
) | ||
|
||
match typeNode.kind with | ||
| Ts.SyntaxKind.TypeOperator -> | ||
let typeOperatorNode = typeNode :?> Ts.TypeOperatorNode | ||
|
||
return reader.ReadTypeOperatorNode typeOperatorNode | ||
|
||
| _ -> return readTypeUsingFlags reader typ | ||
|
||
| _ -> return readTypeUsingFlags reader typ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave another chance to rewrite the TypeQuery
code. Do you think this version of the code is easier to read?
Personally, I think it is easier to read because it remove (at least visually) several level of pattern matching over options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to use the CE to flatten the nested pattern matching but I'd really like to be able to combine and refactor this function and readTypeUsingFlags
to avoid the overlapping logical branches. I couldn't work out an easy way to do it yesterday but if you think it's a good idea too, I'll give it another go.
Maybe it should also be split off into a separate Reader/TypeQueryNode.fs
file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to use the CE to flatten the nested pattern matching but I'd really like to be able to combine and refactor this function and
readTypeUsingFlags
to avoid the overlapping logical branches. I couldn't work out an easy way to do it yesterday but if you think it's a good idea too, I'll give it another go.
I didn't look too much on that, but if you can refactor the code to have a single function/path I am all for it. With or without the CE depending on what works best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MangelMaxime I came up with an alternative rewrite of this code here 031dcd5 I found that just result {}
rather than resultOption
was sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few comments on your commit but I think we are on the good track. If we fix the comments, we should be able to merge it in Glutinum and keep moving forward.
I also gave a chance to re-think the error reporter: let generateReaderError
(fileFolder: string, fileName: string, fileLine: string)
(errorContext: string)
(reason: string)
(node: Ts.Node)
=
let sourceFile = node.getSourceFile ()
let lineAndChar = sourceFile.getLineAndCharacterOfPosition node.pos
let line = int lineAndChar.line + 1
let column = int lineAndChar.character + 1
let errorOrigin =
let fileFolder =
let index = fileFolder.IndexOf("src/Glutinum.Converter")
"./" + fileFolder.Substring(index)
$"%s{fileFolder}/%s{fileName}(%s{fileLine})"
$"""%s{errorOrigin}: Error while reading %s{errorContext} from:
%s{sourceFile.fileName}(%d{line},%d{column})
%s{reason}
--- Text ---
%s{node.getFullText ()}
---
--- Parent text ---
%s{node.parent.getFullText ()}
---""" Example of output:
This allows to ensure that all errors have the same level of information, because it can be easy to forget to add I decided to use a relative path for reporting the origin of the error because it seems like for VSCode it allows to click on the link to go the file declaration. If others IDEs have don't support that, we can do something like: let errorOrigin =
let fileFolder =
#if DEBUG
$"%s{fileFolder}"
#else
let index = fileFolder.IndexOf("src/Glutinum.Converter")
"./" + fileFolder.Substring(index)
#endif like that in DEBUG we generate a full path and in release we have a relative path. The relative path in release, is because showing my system architecture doesn't matter and add noise for no reason. What do you think? If you agree on this proposition, I can make the change to the PR |
In .NET land we could use |
proposed error format looks good. Just a note to normalize |
You would be surprised at how many times I have been surprised by Fable. And you can add +1 to that number because Thanks you for the tips, you have now idea for how long I have been looking for something like that 😅 (probably 5 years). It is really useful, when generating documentation with link to the source code. I just need to rewrite the
Nothing that a good old |
@roboz0r This PR was starting to be a bit messy with the different changes between the I think it is best if you can open a new PR using the latest version of |
New PR #124 |
transforms to
Summary
GlueType.MappedType
ObjectType
toIndexedAccessType
SyntaxKind.name
helper function for debugging__SOURCE_FILE__
literals to errors for debugging