-
-
Notifications
You must be signed in to change notification settings - Fork 279
/
LineLens.fs
155 lines (120 loc) · 5.17 KB
/
LineLens.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
module Ionide.VSCode.FSharp.LineLens
open System
open System.Collections.Generic
open Fable.Core
open Fable.Import.VSCode
open Fable.Import.VSCode.Vscode
open Fable.Core.JsInterop
open DTO
open LineLensShared
type Number = float
let private logger =
ConsoleAndOutputChannelLogger(Some "LineLens", Level.DEBUG, None, Some Level.DEBUG)
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module LineLensConfig =
open System.Text.RegularExpressions
type EnabledMode =
| Never
| ReplaceCodeLens
| Always
let private parseEnabledMode (s: string) =
match s.ToLowerInvariant() with
| "never" -> false
| "always" -> true
| "replacecodelens"
| _ -> true
let defaultConfig = { enabled = true; prefix = " // " }
let private themeRegex = Regex("\s*theme\((.+)\)\s*")
let getConfig () =
let cfg = workspace.getConfiguration ()
let fsharpCodeLensConfig =
cfg.get("[fsharp]", JsObject.empty).tryGet<bool> ("editor.codeLens")
{ enabled = cfg.get ("FSharp.lineLens.enabled", "replacecodelens") |> parseEnabledMode
prefix = cfg.get ("FSharp.lineLens.prefix", defaultConfig.prefix) }
module DecorationUpdate =
let formatSignature (sign: SignatureData) : string =
let formatType =
function
| Contains "->" t -> sprintf "(%s)" t
| t -> t
let args =
sign.Parameters
|> List.map (fun group -> group |> List.map (fun p -> formatType p.Type) |> String.concat " * ")
|> String.concat " -> "
if String.IsNullOrEmpty args then
sign.OutputType
else
args + " -> " + formatType sign.OutputType
let interestingSymbolPositions (symbols: Symbols[]) : DTO.Range[] =
symbols
|> Array.collect (fun syms ->
let interestingNested =
syms.Nested
|> Array.choose (fun sym ->
if
sym.GlyphChar <> "Fc"
&& sym.GlyphChar <> "M"
&& sym.GlyphChar <> "F"
&& sym.GlyphChar <> "P"
|| sym.IsAbstract
|| sym.EnclosingEntity = "I" // interface
|| sym.EnclosingEntity = "R" // record
|| sym.EnclosingEntity = "D" // DU
|| sym.EnclosingEntity = "En" // enum
|| sym.EnclosingEntity = "E" // exception
then
None
else
Some sym.BodyRange)
if syms.Declaration.GlyphChar <> "Fc" then
interestingNested
else
interestingNested |> Array.append [| syms.Declaration.BodyRange |])
let private lineRange (doc: TextDocument) (range: Vscode.Range) =
let textLine = doc.lineAt range.start.line
textLine.range
let private getSignature (uri: Uri) (range: DTO.Range) =
async {
try
let! signaturesResult =
LanguageService.signatureData uri range.StartLine (range.StartColumn - 1)
|> Async.AwaitPromise
return
signaturesResult
|> Option.map (fun r -> range |> CodeRange.fromDTO, formatSignature r.Data)
with e ->
logger.Error("Error getting signature %o", e)
return None
}
let signatureToDecoration
(config: LineLensShared.LineLensConfig)
(doc: TextDocument)
(range: Vscode.Range, signature: string)
=
LineLensShared.LineLensDecorations.create "fsharp.linelens" (lineRange doc range) (config.prefix + signature)
let private onePerLine (ranges: Range[]) =
ranges
|> Array.groupBy (fun r -> r.StartLine)
|> Array.choose (fun (_, ranges) -> if ranges.Length = 1 then Some(ranges.[0]) else None)
let private needUpdate (uri: Uri) (version: Number) { documents = documents } =
(documents |> Documents.tryGetCachedAtVersion uri version).IsSome
let declarationsResultToSignatures document declarationsResult uri =
promise {
let interesting = declarationsResult.Data |> interestingSymbolPositions
let interesting = onePerLine interesting
let! signatures =
interesting
|> Array.map (getSignature uri)
|> Async.Sequential // Need to be sequential otherwise we'll flood the server with requests causing threadpool exhaustion
|> Async.StartAsPromise
|> Promise.map (fun s -> s |> Array.choose (id))
return signatures
}
let private lineLensDecorationUpdate: LineLensShared.DecorationUpdate =
LineLensShared.DecorationUpdate.updateDecorationsForDocument
LanguageService.lineLenses
DecorationUpdate.declarationsResultToSignatures
DecorationUpdate.signatureToDecoration
let createLineLens () =
LineLensShared.LineLens("LineLens", lineLensDecorationUpdate, LineLensConfig.getConfig)
let Instance = createLineLens ()