Skip to content

Commit

Permalink
Add textDocument/inlineValue from LSP 3.17 (#1042)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaashyapan committed Feb 15, 2023
1 parent 2017adc commit fdeca2f
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ test/FsAutoComplete.Tests.Lsp/TestCases/FakeInterop/build.fsx.lock
coverage.xml
coverage
test/FsAutoComplete.Tests.Lsp/TestResults/

.tool-versions
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [0.58.5] - 2023-02-04

* Add textDocument/inlineValue from LSP 3.17
* InlineValue config option to shadow PipelineHint config option
* Fix inlayHints for typed params #1046

## [0.58.4] - 2023-02-04

* Fix crash due to missing dependency on Microsoft.Extensions.Caching.Memory
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ FsAutoComplete supports [LSP](https://microsoft.github.io/language-server-protoc
* `textDocument/documentHighlight`
* `textDocument/signatureHelp`
* `textDocument/documentSymbol`
* `textDocument/inlayHint`
* `textDocument/inlineValue`
* `workspace/didChangeWatchedFiles`
* `workspace/didChangeConfiguration`
* `workspace/symbol`
Expand Down
2 changes: 1 addition & 1 deletion paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ NUGET
System.Collections.Immutable (>= 5.0)
System.Reflection.Metadata (>= 5.0)
Ionide.KeepAChangelog.Tasks (0.1.8) - copy_local: true
Ionide.LanguageServerProtocol (0.4.11)
Ionide.LanguageServerProtocol (0.4.12)
FSharp.Core (>= 6.0)
Newtonsoft.Json (>= 13.0.1)
StreamJsonRpc (>= 2.10.44)
Expand Down
74 changes: 74 additions & 0 deletions src/FsAutoComplete.Core/Commands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,78 @@ module Commands =
tyRes.TryGetToolTip pos lineStr
|> Result.bimap CoreResponse.Res CoreResponse.ErrorRes

// Calculates pipeline hints for now as in fSharp/pipelineHint with a bit of formatting on the hints
let inlineValues (contents: NamedText) (tyRes: ParseAndCheckResults) : Async<(pos * String)[]> =
asyncResult {
// Debug.waitForDebuggerAttached "AdaptiveServer"
let getSignatureAtPos pos =
option {
let! lineStr = contents.GetLine pos

let! tip = tyRes.TryGetToolTip pos lineStr |> Option.ofResult

return TipFormatter.extractGenericParameters tip
}
|> Option.defaultValue []

let areTokensCommentOrWhitespace (tokens: FSharpTokenInfo list) =
tokens
|> List.exists (fun token ->
token.CharClass <> FSharpTokenCharKind.Comment
&& token.CharClass <> FSharpTokenCharKind.WhiteSpace
&& token.CharClass <> FSharpTokenCharKind.LineComment)
|> not

let getStartingPipe =
function
| y :: xs when y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
| x :: y :: xs when x.TokenName.ToUpper() = "WHITESPACE" && y.TokenName.ToUpper() = "INFIX_BAR_OP" -> Some y
| _ -> None

let folder (lastExpressionLine, lastExpressionLineWasPipe, acc) (currentIndex, currentTokens) =
let isCommentOrWhitespace = areTokensCommentOrWhitespace currentTokens

let isPipe = getStartingPipe currentTokens

match isCommentOrWhitespace, isPipe with
| true, _ -> lastExpressionLine, lastExpressionLineWasPipe, acc
| false, Some pipe ->
currentIndex, true, (lastExpressionLine, lastExpressionLineWasPipe, currentIndex, pipe) :: acc
| false, None -> currentIndex, false, acc

// Signature looks like <T> is Async<unit>
let inline removeSignPrefix (s: String) =
s.Split(" is ") |> Array.tryLast |> Option.defaultValue ""

let hints =
Array.init ((contents: ISourceText).GetLineCount()) (fun line -> (contents: ISourceText).GetLineString line)
|> Array.map (Lexer.tokenizeLine [||])
|> Array.mapi (fun currentIndex currentTokens -> currentIndex, currentTokens)
|> Array.fold folder (0, false, [])
|> (fun (_, _, third) -> third |> Array.ofList)
|> Array.Parallel.map (fun (lastExpressionLine, lastExpressionLineWasPipe, currentIndex, pipeToken) ->
let pipePos = Position.fromZ currentIndex pipeToken.RightColumn
let prevLinePos = Position.fromZ lastExpressionLine 70 //We dont have the column on the previous line. So err to the right and let the client display in the right position
let gens = getSignatureAtPos pipePos

if lastExpressionLineWasPipe then
let allS = gens |> List.tryLast |> Option.defaultValue "" |> removeSignPrefix
[| (pipePos, allS) |]
else
match gens with
| [ currentS ] -> [| (pipePos, removeSignPrefix currentS) |]
| [ prevS; currentS ] ->
[| (prevLinePos, removeSignPrefix prevS)
(pipePos, removeSignPrefix currentS) |]
| _ ->
let allS = gens |> Seq.intersperse "; " |> Seq.reduce (+)
[| (pipePos, allS) |])

return (Array.concat hints)
}
|> AsyncResult.foldResult id (fun _ -> [||])


let pipelineHints (tryGetFileSource: _ -> Result<NamedText, _>) (tyRes: ParseAndCheckResults) =
result {
// Debug.waitForDebuggerAttached "AdaptiveServer"
Expand Down Expand Up @@ -2104,6 +2176,8 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers:

FsAutoComplete.Core.InlayHints.provideHints (text, tyRes, range, hintConfig)

static member InlineValues(contents: NamedText, tyRes: ParseAndCheckResults) = Commands.inlineValues contents tyRes

member __.PipelineHints(tyRes: ParseAndCheckResults) =
Commands.pipelineHints state.TryGetFileSource tyRes

Expand Down
16 changes: 12 additions & 4 deletions src/FsAutoComplete.Core/InlayHints.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,13 +1001,21 @@ let provideHints (text: NamedText, parseAndCheck: ParseAndCheckResults, range: R
| _, Some appliedArgRanges ->
let parameters = methodOrConstructor.CurriedParameterGroups |> Seq.concat

let appliedArgRanges = appliedArgRanges |> Array.ofList
let definitionArgs = parameters |> Array.ofSeq

for idx = 0 to appliedArgRanges.Length - 1 do
let appliedArgRange = appliedArgRanges.[idx]
let parms =
appliedArgRanges
|> Array.ofList
|> Array.mapi (fun i v ->
if i < definitionArgs.Length then
Some(definitionArgs.[i], v)
else
None)
|> Array.filter Option.isSome
|> Array.map Option.get

for (definitionArg, appliedArgRange) in parms do
let! appliedArgText = text[appliedArgRange]
let definitionArg = definitionArgs.[idx]

if ShouldCreate.paramHint methodOrConstructor definitionArg appliedArgText then
let hint = createParamHint appliedArgRange definitionArg.DisplayName
Expand Down
26 changes: 26 additions & 0 deletions src/FsAutoComplete/LspHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,10 @@ type InlayHintDto =
parameterNames: bool option
disableLongTooltip: bool option }

type InlineValueDto =
{ Enabled: bool option
Prefix: string option }

type DebugDto =
{ DontCheckRelatedFiles: bool option
CheckFileDebouncerTimeout: int option
Expand Down Expand Up @@ -637,6 +641,7 @@ type FSharpConfigDto =
AbstractClassStubGenerationObjectIdentifier: string option
AbstractClassStubGenerationMethodBody: string option
CodeLenses: CodeLensConfigDto option
PipelineHints: InlineValueDto option
InlayHints: InlayHintDto option
Debug: DebugDto option }

Expand All @@ -660,6 +665,14 @@ type InlayHintsConfig =
parameterNames = true
disableLongTooltip = true }

type InlineValuesConfig =
{ Enabled: bool option
Prefix: string option }

static member Default =
{ Enabled = Some true
Prefix = Some "//" }

type DebugConfig =
{ DontCheckRelatedFiles: bool
CheckFileDebouncerTimeout: int
Expand Down Expand Up @@ -708,6 +721,7 @@ type FSharpConfig =
GenerateBinlog: bool
CodeLenses: CodeLensConfig
InlayHints: InlayHintsConfig
InlineValues: InlineValuesConfig
Debug: DebugConfig }

static member Default: FSharpConfig =
Expand Down Expand Up @@ -746,6 +760,7 @@ type FSharpConfig =
GenerateBinlog = false
CodeLenses = CodeLensConfig.Default
InlayHints = InlayHintsConfig.Default
InlineValues = InlineValuesConfig.Default
Debug = DebugConfig.Default }

static member FromDto(dto: FSharpConfigDto) : FSharpConfig =
Expand Down Expand Up @@ -803,6 +818,14 @@ type FSharpConfig =
{ typeAnnotations = defaultArg ihDto.typeAnnotations true
parameterNames = defaultArg ihDto.parameterNames true
disableLongTooltip = defaultArg ihDto.disableLongTooltip true }

InlineValues =
match dto.PipelineHints with
| None -> InlineValuesConfig.Default
| Some ivDto ->
{ Enabled = ivDto.Enabled |> Option.defaultValue true |> Some
Prefix = ivDto.Prefix |> Option.defaultValue "//" |> Some }

Debug =
match dto.Debug with
| None -> DebugConfig.Default
Expand Down Expand Up @@ -882,6 +905,9 @@ type FSharpConfig =
{ typeAnnotations = defaultArg ihDto.typeAnnotations x.InlayHints.typeAnnotations
parameterNames = defaultArg ihDto.parameterNames x.InlayHints.parameterNames
disableLongTooltip = defaultArg ihDto.disableLongTooltip x.InlayHints.disableLongTooltip }
InlineValues =
{ Enabled = defaultArg (dto.PipelineHints |> Option.map (fun n -> n.Enabled)) x.InlineValues.Enabled
Prefix = defaultArg (dto.PipelineHints |> Option.map (fun n -> n.Prefix)) x.InlineValues.Prefix }
Debug =
match dto.Debug with
| None -> DebugConfig.Default
Expand Down
49 changes: 48 additions & 1 deletion src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,17 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
|> Option.map FSharpConfig.FromDto
|> Option.defaultValue FSharpConfig.Default

logger.info (
Log.setMessage "Intialization options {items}"
>> Log.addContextDestructured "items" c
)

let inlineValueToggle =
match c.InlineValues.Enabled with
| Some true -> Some { ResolveProvider = Some false }
| Some false -> None
| None -> None

let actualRootPath =
match p.RootUri with
| Some rootUri -> Some(Path.FileUriToLocalPath rootUri)
Expand Down Expand Up @@ -3170,14 +3181,50 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar
return (Some hints)
with e ->
logger.error (
Log.setMessage "TextDocumentSemanticTokensRange Request Errored {p}"
Log.setMessage "TextDocumentInlayHint Request Errored {p}"
>> Log.addContextDestructured "p" p
>> Log.addExn e
)

return! LspResult.internalError (string e)
}

override x.TextDocumentInlineValue(p) =
asyncResult {
try
logger.info (
Log.setMessage "TextDocumentInlineValue Request: {parms}"
>> Log.addContextDestructured "parms" p
)

let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath
let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr


let! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr

let fcsRange = protocolRangeToRange (UMX.untag filePath) p.Range

let! pipelineHints = Commands.InlineValues(namedText.Lines, tyRes)

let hints =
pipelineHints
|> Array.map (fun (pos, linehints) ->
{ InlineValueText.Range = fcsPosToProtocolRange pos
Text = linehints }
|> InlineValue.InlineValueText)
|> Some

return hints
with e ->
logger.error (
Log.setMessage "TextDocumentInlineValue Request Errored {p}"
>> Log.addContextDestructured "p" p
>> Log.addExn e
)

return! LspResult.internalError (string e)
}

//unsupported -- begin
override x.CodeActionResolve(p) =
Expand Down
32 changes: 32 additions & 0 deletions src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,10 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) =

updateConfig c

logger.info (
Log.setMessage "Intialization options {items}"
>> Log.addContextDestructured "items" c
)

clientCapabilities <- p.Capabilities
lspClient.ClientCapabilities <- clientCapabilities
Expand All @@ -1095,6 +1099,11 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) =
| Some tyRes -> return tyRes, lineAtPos, fileLines
}

let inlineValueToggle =
match c.InlineValues.Enabled with
| Some true -> Some { ResolveProvider = Some false }
| Some false -> None
| None -> None

let getFileLines = commands.TryGetFileCheckerOptionsWithLines >> Result.map snd

Expand Down Expand Up @@ -2837,6 +2846,29 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) =
|> success
|> async.Return)

override x.TextDocumentInlineValue(p: InlineValueParams) : AsyncLspResult<InlineValue[] option> =
logger.info (
Log.setMessage "TextDocumentInlineValue Request: {parms}"
>> Log.addContextDestructured "parms" p
)

p.TextDocument
|> x.fileHandler (fun fn tyRes lines ->
async {
let fcsRange = protocolRangeToRange (UMX.untag fn) p.Range

let! pipelineHints = Commands.InlineValues(lines, tyRes)

let hints =
pipelineHints
|> Array.map (fun (pos, linehints) ->
{ InlineValueText.Range = fcsPosToProtocolRange pos
Text = (linehints.ToString()) }
|> InlineValue.InlineValueText)

return success (Some hints)
})

override x.Dispose() =
(x :> ILspServer).Shutdown() |> Async.Start

Expand Down
13 changes: 13 additions & 0 deletions test/FsAutoComplete.Tests.Lsp/Helpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ let defaultConfigDto: FSharpConfigDto =
{ typeAnnotations = Some true
parameterNames = Some true
disableLongTooltip = Some true }
PipelineHints =
Some
{ Enabled = Some true
Prefix = Some "//" }
Debug = None }

let clientCaps: ClientCapabilities =
Expand All @@ -243,6 +247,9 @@ let clientCaps: ClientCapabilities =
let inlayHintCaps: InlayHintWorkspaceClientCapabilities =
{ RefreshSupport = Some false }

let inlineValueCaps: InlineValueWorkspaceClientCapabilities =
{ RefreshSupport = Some false }

let codeLensCaps: CodeLensWorkspaceClientCapabilities =
{ RefreshSupport = Some true }

Expand All @@ -253,6 +260,7 @@ let clientCaps: ClientCapabilities =
Symbol = Some symbolCaps
SemanticTokens = Some semanticTokenCaps
InlayHint = Some inlayHintCaps
InlineValue = Some inlineValueCaps
CodeLens = Some codeLensCaps }

let textCaps: TextDocumentClientCapabilities =
Expand Down Expand Up @@ -328,6 +336,10 @@ let clientCaps: ClientCapabilities =
{ DynamicRegistration = Some true
ResolveSupport = None }

let inlineValueCaps: InlineValueClientCapabilities =
{ DynamicRegistration = Some true
ResolveSupport = None }

let renameCaps: RenameClientCapabilities =
{ DynamicRegistration = Some true
HonorsChangeAnnotations = Some false
Expand All @@ -353,6 +365,7 @@ let clientCaps: ClientCapabilities =
SelectionRange = Some dynCaps
SemanticTokens = Some semanticTokensCaps
InlayHint = Some inlayHintCaps }
// InlineValue = Some inlineValueCaps }


{ Workspace = Some workspaceCaps
Expand Down
Loading

0 comments on commit fdeca2f

Please sign in to comment.