Skip to content
This repository was archived by the owner on Jan 3, 2019. It is now read-only.

Commit cb7ea08

Browse files
committed
Merge pull request #303 from fsharp/fsi-path-completions
added path completions to fsi
2 parents c1978a8 + 2662f24 commit cb7ea08

File tree

4 files changed

+96
-7
lines changed

4 files changed

+96
-7
lines changed

MonoDevelop.FSharp.Shared/Completions.fs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
open Microsoft.FSharp.Compiler.SourceCodeServices
44
open Microsoft.FSharp.Compiler.Interactive.Shell
5+
open System
6+
open System.IO
7+
open System.Text.RegularExpressions
58

69
type CompletionData = {
710
displayText: string
@@ -12,7 +15,19 @@ type CompletionData = {
1215
description: string
1316
}
1417

18+
type PathCompletion = {
19+
paths: string seq
20+
residue: string
21+
}
22+
1523
module Completion =
24+
let (|FilePath|_|) line =
25+
let matches = Regex.Matches(line, "^\s*#(load|r)\s+\"([^\"]*)", RegexOptions.Compiled)
26+
if matches.Count > 0 then
27+
Some (matches.[0].Groups.[1].Value, matches.[0].Groups.[2].Value)
28+
else
29+
None
30+
1631
let rec allBaseTypes (entity:FSharpEntity) =
1732
seq {
1833
match entity.TryFullName with
@@ -107,12 +122,47 @@ module Completion =
107122
]
108123

109124
let mutable symbolList = List.empty
125+
126+
let getPaths directive (path: string) =
127+
seq {
128+
// absolute paths
129+
if (Directory.Exists path) then
130+
yield! Directory.EnumerateDirectories path
131+
132+
if directive = "load" then
133+
yield! Directory.EnumerateFiles(path, "*.fsx")
134+
135+
if directive = "r" then
136+
yield! Directory.EnumerateFiles(path, "*.dll")
137+
}
138+
139+
let getPathCompletion (workingFolder: string option) directive (path: string) =
140+
let separatorIndex = path.LastIndexOf Path.DirectorySeparatorChar
141+
let pathToSeparator, residue, offset =
142+
if separatorIndex > -1 then
143+
path.[0..separatorIndex], path.[separatorIndex..], 0
144+
else
145+
"", path, 1
146+
147+
let paths = seq {
148+
if Path.IsPathRooted pathToSeparator then
149+
yield! getPaths directive pathToSeparator
150+
|> Seq.map(fun path -> path.[separatorIndex..])
151+
// relative to working folder
152+
elif workingFolder.IsSome then
153+
let relpath = Path.Combine (workingFolder.Value, pathToSeparator)
154+
yield! getPaths directive relpath
155+
|> Seq.map(fun path -> path.[separatorIndex+workingFolder.Value.Length+1+offset..])
156+
}
157+
158+
{ residue=residue; paths=paths }
159+
110160
let getCompletions (fsiSession: FsiEvaluationSession, input:string, column: int) =
111161
async {
112162
let parseResults, checkResults, _checkProjectResults = fsiSession.ParseAndCheckInteraction(input)
113163
let longName,residue = Parsing.findLongIdentsAndResidue(column, input)
114164
let! symbols = checkResults.GetDeclarationListSymbols(Some parseResults, 1, column, input, longName, residue, fun (_,_) -> false)
115-
let results = symbols
165+
let results = symbols
116166
|> List.choose symbolToCompletionData
117167

118168
let completions, symbols = results |> List.unzip
@@ -140,6 +190,8 @@ module Completion =
140190
EmptyTip
141191
}
142192

193+
194+
143195
let getParameterHints (fsiSession: FsiEvaluationSession, input:string, column: int) =
144196
async {
145197
let _parseResults, checkResults, _checkProjectResults = fsiSession.ParseAndCheckInteraction("();;")

MonoDevelop.FSharpBinding/FSharpInteractivePad.fs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ type FsiDocumentContext() =
6060
let mutable editor:TextEditor = null
6161

6262
let contextChanged = DelegateEvent<_>()
63+
let mutable workingFolder: string option = None
6364
do
6465
project.FileName <- FilePath name
66+
6567
override x.ParsedDocument = pd
6668
override x.AttachToProject(_) = ()
6769
override x.ReparseDocument() = ()
@@ -73,7 +75,9 @@ type FsiDocumentContext() =
7375

7476
member x.CompletionWidget with set (value) = completionWidget <- value
7577
member x.Editor with set (value) = editor <- value
76-
78+
member x.WorkingFolder
79+
with get() = workingFolder
80+
and set(folder) = workingFolder <- folder
7781
interface ICompletionWidget with
7882
member x.CaretOffset
7983
with get() = completionWidget.CaretOffset
@@ -151,10 +155,12 @@ type FSharpInteractivePad() =
151155
let newLineIcon = ImageService.GetIcon("md-template")
152156

153157
let getCorrectDirectory () =
154-
if IdeApp.Workbench.ActiveDocument <> null && FileService.isInsideFSharpFile() then
155-
let doc = IdeApp.Workbench.ActiveDocument.FileName.ToString()
156-
if doc <> null then Path.GetDirectoryName(doc) |> Some else None
157-
else None
158+
ctx.WorkingFolder <-
159+
if IdeApp.Workbench.ActiveDocument <> null && FileService.isInsideFSharpFile() then
160+
let doc = IdeApp.Workbench.ActiveDocument.FileName.ToString()
161+
if doc <> null then Path.GetDirectoryName(doc) |> Some else None
162+
else None
163+
ctx.WorkingFolder
158164

159165
let nonBreakingSpace = "\u00A0" // used to disable editor syntax highlighting for output
160166

MonoDevelop.FSharpBinding/FSharpParsedDocument.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ type FSharpParsedDocument(fileName, location: DocumentLocation option) =
2323
[<AutoOpen>]
2424
module DocumentContextExt =
2525
type DocumentContext with
26+
member x.GetWorkingFolder() =
27+
if IdeApp.Workbench.ActiveDocument <> null && FileService.isInsideFSharpFile() then
28+
let doc = IdeApp.Workbench.ActiveDocument.FileName.ToString()
29+
if doc <> null then Path.GetDirectoryName(doc) |> Some else None
30+
else None
31+
2632
member x.TryGetFSharpParsedDocument() =
2733
x.TryGetParsedDocument()
2834
|> Option.bind (function :? FSharpParsedDocument as fpd -> Some fpd | _ -> None)

MonoDevelop.FSharpBinding/FSharpTextEditorCompletion.fs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ module Completion =
162162
else
163163
None
164164

165+
let (|FilePath|_|) context =
166+
let matches = Regex.Matches(context.lineToCaret, "^\s*#(load|r)\s+\"([^\"]*)", RegexOptions.Compiled)
167+
if matches.Count > 0 then
168+
Some (matches.[0].Groups.[1].Value, matches.[0].Groups.[2].Value)
169+
else
170+
None
171+
165172
let (|LetIdentifier|_|) context =
166173
if Regex.IsMatch(context.lineToCaret, "\s?(let!?|override|member)\s+[^=]+$", RegexOptions.Compiled) then
167174
let document = new TextDocument(context.lineToCaret)
@@ -460,6 +467,16 @@ module Completion =
460467
return CompletionDataList()
461468
}
462469

470+
let getCompletionList (completions: MonoDevelop.FSharp.Shared.PathCompletion) =
471+
let result = CompletionDataList()
472+
result.DefaultCompletionString <- completions.residue
473+
result.TriggerWordLength <- completions.residue.Length
474+
let completions =
475+
completions.paths
476+
|> Seq.map (fun path -> CompletionData(path))
477+
result.AddRange completions
478+
result
479+
463480
let getModifiers context =
464481
let {
465482
column = column
@@ -498,7 +515,15 @@ module Completion =
498515

499516
let! results = async {
500517
match completionContext with
501-
| InvalidToken
518+
| FilePath (directive, path) ->
519+
let workingFolder =
520+
match documentContext |> Option.tryCast<FsiDocumentContext> with
521+
| Some ctx -> ctx.WorkingFolder
522+
| _ -> documentContext.GetWorkingFolder()
523+
524+
let completions = MonoDevelop.FSharp.Shared.Completion.getPathCompletion workingFolder directive path
525+
return getCompletionList completions
526+
| InvalidToken
502527
| InvalidCompletionChar
503528
| DoubleDot
504529
| LiteralNumber

0 commit comments

Comments
 (0)