Skip to content

Commit

Permalink
Enable support for tagging single-string-parameter functions' argumen…
Browse files Browse the repository at this point in the history
…ts as that function's named language
  • Loading branch information
baronfel committed Dec 29, 2023
1 parent 80bcbc9 commit 0f2d918
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 6 deletions.
73 changes: 69 additions & 4 deletions src/FsAutoComplete.Core/NestedLanguages.fs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ let rec private (|IsApplicationWithStringParameters|_|) (e: SynExpr) : option<St
// method call with multiple string or interpolated string parameters (this also covers the case when not all parameters of the member are strings)
// c.M("<div>", true) and/or c.M(true, "<div>")
// piped method call with multiple string or interpolated string parameters (this also covers the case when not all parameters of the member are strings)
// let binding that is a string value that has the stringsyntax attribute on it - [<StringSyntax("html")>] let html = "<div />"
// let binding that is a string value that has the StringSyntax attribute on it - [<StringSyntax("html")>] let html = "<div />"
// all of the above but with literals
| _ -> None

Expand Down Expand Up @@ -158,7 +158,7 @@ let private parametersThatAreStringSyntax
match
checkResults.GetSymbolUseAtLocation(
endOfFinalTextToken.Line,
endOfFinalTextToken.Column - 1,
endOfFinalTextToken.Column - 1, // TODO: check off-by-one here?
lineText,
precedingParts |> List.map (fun i -> i.idText)
)
Expand All @@ -171,7 +171,7 @@ let private parametersThatAreStringSyntax
)

let sym = usage.Symbol
// todo: keep MRU map of symbols to parameters and MRU of parameters to stringsyntax status
// todo: keep MRU map of symbols to parameters and MRU of parameters to StringSyntax status

match sym with
| :? FSharpMemberOrFunctionOrValue as mfv ->
Expand All @@ -194,6 +194,71 @@ let private parametersThatAreStringSyntax
return returnVal.ToArray()
}

let private hasSingleStringParameter (
parameters: StringParameter[],

Check notice

Code scanning / Ionide.Analyzers.Cli

Detect if generic type should be in the postfix position. Note

Prefer postfix syntax for arrays.
checkResults: FSharpCheckFileResults,
text: VolatileFile
) : Async<NestedLanguageDocument[]> =

Check notice

Code scanning / Ionide.Analyzers.Cli

Detect if generic type should be in the postfix position. Note

Prefer postfix syntax for arrays.
async {
let returnVal = ResizeArray()

for p in parameters do
logger.info (
Log.setMessageI
$"Checking parameter: {p.parameterRange.ToString():range} in member {p.methodIdent.ToString():methodName} of {text.FileName:filename}@{text.Version:version} -> {text.Source[p.parameterRange]:sourceText}"
)

let lastPart = p.methodIdent[^0]
let endOfFinalTextToken = lastPart.idRange.End

match text.Source.GetLine(endOfFinalTextToken) with
| None -> ()
| Some lineText ->

match
checkResults.GetSymbolUseAtLocation(
endOfFinalTextToken.Line,
endOfFinalTextToken.Column + 1,
lineText,
p.methodIdent |> List.map (fun x -> x.idText)
)
with
| None -> ()
| Some usage ->
logger.info (
Log.setMessageI
$"Found symbol use: {usage.Symbol.ToString():symbol} in member {p.methodIdent.ToString():methodName} of {text.FileName:filename}@{text.Version:version} -> {text.Source[p.parameterRange]:sourceText}"
)

let sym = usage.Symbol
// todo: keep MRU map of symbols to parameters and MRU of parameters to StringSyntax status

match sym with
| :? FSharpMemberOrFunctionOrValue as mfv ->
let languageName = sym.DisplayName // TODO: what about funky names?
let allParameters = mfv.CurriedParameterGroups |> Seq.collect id
let firstParameter = allParameters |> Seq.tryHead
let hasOthers = allParameters |> Seq.skip 1 |> Seq.isEmpty |> not
match hasOthers, firstParameter with
| _, None -> ()
| true, _ -> ()
| false, Some fsharpP ->
logger.info (
Log.setMessageI
$"Found parameter: {fsharpP.ToString():symbol} with {fsharpP.Attributes.Count:attributeCount} in member {p.methodIdent.ToString():methodName} of {text.FileName:filename}@{text.Version:version} -> {text.Source[p.parameterRange]:sourceText}"
)
let baseType = fsharpP.Type.StripAbbreviations()
if baseType.BasicQualifiedName = "System.String" then
returnVal.Add
{ Language = languageName
Ranges = rangeMinusRanges p.parameterRange p.rangesToRemove }
else
()
| _ -> ()

return returnVal.ToArray()
}

/// to find all of the nested language highlights, we're going to do the following:
/// * find all of the interpolated strings or string literals in the file that are in parameter-application positions
/// * get the method calls happening at those positions to check if that method has the StringSyntaxAttribute
Expand All @@ -214,7 +279,7 @@ let findNestedLanguages (tyRes: ParseAndCheckResults, text: VolatileFile) : Nest
$"Potential parameter: {p.parameterRange.ToString():range} in member {p.methodIdent.ToString():methodName} of {text.FileName:filename}@{text.Version:version} -> {text.Source[p.parameterRange]:sourceText}"
)

let! actualStringSyntaxParameters = parametersThatAreStringSyntax (potentialParameters, tyRes.GetCheckResults, text)
let! actualStringSyntaxParameters = hasSingleStringParameter (potentialParameters, tyRes.GetCheckResults, text) // || parametersThatAreStringSyntax (potentialParameters, tyRes.GetCheckResults, text)

logger.info (
Log.setMessageI
Expand Down
39 changes: 37 additions & 2 deletions test/FsAutoComplete.Tests.Lsp/NestedLanguageTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,47 @@ let tests state =
[| ("uri", [| (5, 31), (5, 51) |]) |]
server ])

fserverTestList "let bound function member" state defaultConfigDto None (fun server ->
serverTestList "let bound function member" state defaultConfigDto None (fun server ->
[ hasLanguages
"with single string parameter"
"""
let foo ([<System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("uri")>] uriString: string) = ()
let u = foo "https://google.com"
"""
[| ("uri", [| (5, 31), (5, 51) |]) |]
server ]) ] ]
server ]) ]
ftestList "Sql" [
serverTestList "loose function" state defaultConfigDto None (fun server -> [
hasLanguages
"with single string parameter and string literal"
"""
let foo (s: string) = ()
let u = foo "https://google.com"
"""
[| ("foo", [| (2, 24), (2, 44) |]) |]
server

hasLanguages
"with single string parameter and interpolated string literal"
"""
let foo (s: string) = ()
let u = foo $"https://{true}.com"
"""
[| ("foo", [| (2, 24), (2, 35)
(2,39), (2, 45) |]) |]
server

hasLanguages
"multiple lanuages in the same document"
"""
let html (s: string) = ()
let sql (s: string) = ()
let myWebPage = html "<body>WOWEE</body>"
let myQuery = sql "select * from accounts where net_worth > 1000000"
"""
[| ("html", [| (3, 33), (3, 53) |])
("sql", [| (4, 30), (4, 80) |]) |]
server
]
)
]]

0 comments on commit 0f2d918

Please sign in to comment.