Skip to content

Commit 93e2f63

Browse files
Maxime MangelMaxime Mangel
andauthored
Add "Rename file" to the solution explorer (#1848)
Co-authored-by: Maxime Mangel <[email protected]>
1 parent 6c485f6 commit 93e2f63

File tree

5 files changed

+90
-9
lines changed

5 files changed

+90
-9
lines changed

release/package.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@
207207
},
208208
"title": "Move file down"
209209
},
210+
{
211+
"command": "fsharp.explorer.renameFile",
212+
"title": "Rename file"
213+
},
210214
{
211215
"command": "fsharp.explorer.removeFile",
212216
"title": "Remove file"
@@ -929,6 +933,10 @@
929933
"command": "fsharp.explorer.moveDown",
930934
"when": "false"
931935
},
936+
{
937+
"command": "fsharp.explorer.renameFile",
938+
"when": "false"
939+
},
932940
{
933941
"command": "fsharp.explorer.removeFile",
934942
"when": "false"
@@ -1207,9 +1215,14 @@
12071215
"group": "inline@2",
12081216
"when": "viewItem == ionide.projectExplorer.file"
12091217
},
1218+
{
1219+
"command": "fsharp.explorer.renameFile",
1220+
"group": "2_action@1",
1221+
"when": "viewItem == ionide.projectExplorer.file"
1222+
},
12101223
{
12111224
"command": "fsharp.explorer.removeFile",
1212-
"group": "1_navigation@4",
1225+
"group": "2_action@2",
12131226
"when": "viewItem == ionide.projectExplorer.file"
12141227
},
12151228
{

src/Components/SolutionExplorer.fs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,14 @@ module SolutionExplorer =
630630

631631
opts.validateInput <-
632632
fun userInput ->
633+
// Add .fs extension if not present
634+
// This is to mirror the logic from the command as we don't force
635+
// user to provide the extension and auto add it if needed
636+
// Maxime Mangel:
637+
// I am not sure if this is the best way to do it because an fsproj
638+
// can have other file extensions than .fs
639+
let userInput = handleUntitled userInput
640+
633641
let fileExist =
634642
existingFiles
635643
|> List.tryFind (fun file ->
@@ -769,6 +777,15 @@ module SolutionExplorer =
769777
| None -> window.showErrorMessage ("No open folder.") |> ignore
770778
}
771779

780+
let private cleanTextEditorsDecorations (filePath : string) =
781+
782+
// VSCode seems to used Unix style paths
783+
let normalizedPath = filePath.Replace("\\", "/")
784+
let fileUri = vscode.Uri.parse $"file:///%s{normalizedPath}"
785+
786+
LineLens.removeDocument fileUri
787+
PipelineHints.removeDocument fileUri
788+
772789
let activate (context: ExtensionContext) =
773790
let emiter = vscode.EventEmitter.Create<_>()
774791
let rootChanged = vscode.EventEmitter.Create<Model>()
@@ -836,18 +853,49 @@ module SolutionExplorer =
836853
// Need to compute the relative path from the project in order to match the user input
837854
let relativeFilePathFromProject = node.path.relative (projDir, filePath)
838855

839-
// Step 1. Remove file from the fsproj
840856
do! FsProjEdit.removeFilePath proj relativeFilePathFromProject
841857

842-
// Step 2. Clean text editor decorations
858+
cleanTextEditorsDecorations filePath
859+
}
860+
| _ -> undefined
861+
|> ignore
843862

844-
// VSCode seems to used Unix style paths
845-
let normalizedPath = filePath.Replace("\\", "/")
846-
let fileUri = vscode.Uri.parse $"file:///%s{normalizedPath}"
863+
None)
864+
)
865+
|> context.Subscribe
847866

848-
LineLens.removeDocument fileUri
849-
PipelineHints.removeDocument fileUri
850-
}
867+
commands.registerCommand (
868+
"fsharp.explorer.renameFile",
869+
objfy2 (fun m ->
870+
match unbox m with
871+
| File(parent, filePath, _, Some virtualPath, _) ->
872+
match parent.Value with
873+
| Some model ->
874+
match tryFindParentProject model with
875+
| Some(Project(_, proj, _, files, _, _, _, _)) ->
876+
createNewFileDialg proj files "Enter a new name"
877+
|> Promise.ofThenable
878+
|> Promise.bind (fun newFileNameOpt ->
879+
promise {
880+
match newFileNameOpt with
881+
| Some newFileName ->
882+
let newFileName = handleUntitled newFileName
883+
884+
do! FsProjEdit.renameFile proj virtualPath newFileName
885+
886+
cleanTextEditorsDecorations filePath
887+
| None -> ()
888+
889+
return null
890+
}
891+
)
892+
|> Promise.catchEnd (fun error ->
893+
window.showErrorMessage error.Message
894+
|> ignore
895+
)
896+
| _ -> undefined
897+
| _ ->
898+
undefined
851899
| _ -> undefined
852900
|> ignore
853901

src/Core/DTO.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,8 @@ module DTO =
426426
{ FsProj: string
427427
FileVirtualPath: string
428428
NewFile: string }
429+
430+
type DotnetRenameFileRequest =
431+
{ FsProj: string
432+
OldFileVirtualPath: string
433+
NewFileName: string }

src/Core/FsProjEdit.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ module FsProjEdit =
1414
let moveFileDownPath project file =
1515
LanguageService.fsprojMoveFileDown project file
1616

17+
let renameFile project file newFileName =
18+
LanguageService.fsprojRenameFile project file newFileName
19+
1720
let removeFilePath project file =
1821
LanguageService.fsprojRemoveFile project file
1922

src/Core/LanguageService.fs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,18 @@ Consider:
440440

441441
cl.sendRequest ("fsproj/addExistingFile", req) |> Promise.map ignore
442442

443+
let fsprojRenameFile (fsproj: string) (file: string) (newFile: string) =
444+
match client with
445+
| None -> Promise.empty
446+
| Some cl ->
447+
let req: FsProj.DotnetRenameFileRequest =
448+
{ FsProj = fsproj
449+
OldFileVirtualPath = file
450+
NewFileName = newFile }
451+
452+
cl.sendRequest ("fsproj/renameFile", req)
453+
|> Promise.map ignore
454+
443455
let fsprojRemoveFile (fsproj: string) (file: string) =
444456
match client with
445457
| None -> Promise.empty

0 commit comments

Comments
 (0)