Skip to content

Commit

Permalink
Don't add the same file a second time if already present in the fsproj (
Browse files Browse the repository at this point in the history
#1076)

Fix #1004

Co-authored-by: Maxime Mangel <[email protected]>
  • Loading branch information
MangelMaxime and Maxime Mangel authored Mar 13, 2023
1 parent 59d38b8 commit 5e28e81
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 50 deletions.
58 changes: 42 additions & 16 deletions src/FsAutoComplete.Core/Commands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ module Commands =

(File.Open(newFilePath, FileMode.OpenOrCreate)).Close()

FsProjEditor.addFile fsprojPath fileVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addFile fsprojPath fileVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg
with ex ->
return CoreResponse.ErrorRes ex.Message
}
Expand All @@ -129,8 +132,12 @@ module Commands =
(File.Open(newFilePath, FileMode.OpenOrCreate)).Close()

let newVirtPath = Path.Combine(virtPathDir, newFileName)
FsProjEditor.addFileAbove fsprojPath fileVirtPath newVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addFileAbove fsprojPath fileVirtPath newVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg

with ex ->
return CoreResponse.ErrorRes ex.Message
}
Expand All @@ -151,8 +158,11 @@ module Commands =
(File.Open(newFilePath, FileMode.OpenOrCreate)).Close()

let newVirtPath = Path.Combine(virtPathDir, newFileName)
FsProjEditor.addFileBelow fsprojPath fileVirtPath newVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addFileBelow fsprojPath fileVirtPath newVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg
with ex ->
return CoreResponse.ErrorRes ex.Message
}
Expand All @@ -165,8 +175,11 @@ module Commands =

let addExistingFile fsprojPath fileVirtPath =
async {
FsProjEditor.addExistingFile fsprojPath fileVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addExistingFile fsprojPath fileVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg
}

let getRangesAtPosition (getParseResultsForFile: _ -> Async<Result<FSharpParseFileResults, _>>) file positions =
Expand Down Expand Up @@ -1455,8 +1468,12 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers:
(File.Open(newFilePath, FileMode.OpenOrCreate)).Close()

let newVirtPath = Path.Combine(virtPathDir, newFileName)
FsProjEditor.addFileAbove fsprojPath fileVirtPath newVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addFileAbove fsprojPath fileVirtPath newVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg

with ex ->
return CoreResponse.ErrorRes ex.Message
}
Expand All @@ -1477,8 +1494,11 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers:
(File.Open(newFilePath, FileMode.OpenOrCreate)).Close()

let newVirtPath = Path.Combine(virtPathDir, newFileName)
FsProjEditor.addFileBelow fsprojPath fileVirtPath newVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addFileBelow fsprojPath fileVirtPath newVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg
with ex ->
return CoreResponse.ErrorRes ex.Message
}
Expand All @@ -1496,16 +1516,22 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers:

(File.Open(newFilePath, FileMode.OpenOrCreate)).Close()

FsProjEditor.addFile fsprojPath fileVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addFile fsprojPath fileVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg
with ex ->
return CoreResponse.ErrorRes ex.Message
}

member _.FsProjAddExistingFile (fsprojPath: string) (fileVirtPath: string) =
async {
FsProjEditor.addExistingFile fsprojPath fileVirtPath
return CoreResponse.Res()
let result = FsProjEditor.addExistingFile fsprojPath fileVirtPath

match result with
| Ok() -> return CoreResponse.Res()
| Error msg -> return CoreResponse.ErrorRes msg
}

member _.FsProjRemoveFile (fsprojPath: string) (fileVirtPath: string) (fullPath: string) =
Expand Down
120 changes: 86 additions & 34 deletions src/FsAutoComplete.Core/FsprojEdit.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,48 @@ open System.IO

module FsProjEditor =

/// <summary>
/// Check if the provided file is already included in the project via a <Compile Include="..." /> tag
/// </summary>
/// <param name="doc">Document to search in</param>
/// <param name="searchedFile">File to search for</param>
/// <returns>
/// Returns true if the file is already included in the project via a <Compile Include="..." /> tag.
///
/// Otherwise returns false.
/// </returns>
let private fileAlreadyIncludedViaCompileTag (doc: System.Xml.XmlDocument) (searchedFile: string) =

let inline sanitazeFile (file: string) = file.Replace("\\", "/")

let searchedFile = sanitazeFile searchedFile

// Take all the <Compile Include="..." /> nodes
doc.SelectNodes("//Compile[@Include]")
|> Seq.cast<System.Xml.XmlNode>
// Find the node that match the file we want to remove
// Note: We sanitaze the file name path because it can changes depending on the OS
// and also on if the user used / or \ as the separator
|> Seq.tryFind (fun node ->
let sanitazedInclude = node.Attributes.["Include"].InnerText |> sanitazeFile

sanitazedInclude = searchedFile)
|> Option.isSome


/// <summary>
/// Create a new <Compile Include="..." /> node
/// </summary>
/// <param name="doc">XmlDocument instance to for which we want to create the node</param>
/// <param name="includePath">Name of the path to include</param>
/// <returns>
/// Returns a new <Compile Include="..." /> node
/// </returns>
let private createNewCompileNode (doc: System.Xml.XmlDocument) (includePath: string) =
let node = doc.CreateElement("Compile")
node.SetAttribute("Include", includePath)
node

let moveFileUp (fsprojPath: string) (file: string) =
let xdoc = System.Xml.XmlDocument()
xdoc.Load fsprojPath
Expand Down Expand Up @@ -36,57 +78,67 @@ module FsProjEditor =
itemGroup.InsertAfter(node, downNode) |> ignore
xdoc.Save fsprojPath

let private createNewCompileNode (doc: System.Xml.XmlDocument) (includePath: string) =
let node = doc.CreateElement("Compile")
node.SetAttribute("Include", includePath)
node

let addFileAbove (fsprojPath: string) (aboveFile: string) (newFileName: string) =
let xdoc = System.Xml.XmlDocument()
xdoc.Load fsprojPath
let xpath = sprintf "//Compile[@Include='%s']/.." aboveFile
let itemGroup = xdoc.SelectSingleNode(xpath)
let childXPath = sprintf "//Compile[@Include='%s']" aboveFile
let aboveNode = itemGroup.SelectSingleNode(childXPath)
let node = createNewCompileNode xdoc newFileName
itemGroup.InsertBefore(node, aboveNode) |> ignore
xdoc.Save fsprojPath

if fileAlreadyIncludedViaCompileTag xdoc newFileName then
Error "File already included in the project"
else
let xpath = sprintf "//Compile[@Include='%s']/.." aboveFile
let itemGroup = xdoc.SelectSingleNode(xpath)
let childXPath = sprintf "//Compile[@Include='%s']" aboveFile
let aboveNode = itemGroup.SelectSingleNode(childXPath)
let node = createNewCompileNode xdoc newFileName
itemGroup.InsertBefore(node, aboveNode) |> ignore
xdoc.Save fsprojPath
Ok()

let addFileBelow (fsprojPath: string) (belowFile: string) (newFileName: string) =
let xdoc = System.Xml.XmlDocument()
xdoc.Load fsprojPath
let xpath = sprintf "//Compile[@Include='%s']/.." belowFile
let itemGroup = xdoc.SelectSingleNode(xpath)
let childXPath = sprintf "//Compile[@Include='%s']" belowFile
let aboveNode = itemGroup.SelectSingleNode(childXPath)
let node = createNewCompileNode xdoc newFileName
itemGroup.InsertAfter(node, aboveNode) |> ignore
xdoc.Save fsprojPath

if fileAlreadyIncludedViaCompileTag xdoc newFileName then
Error "File already included in the project"
else
let xpath = sprintf "//Compile[@Include='%s']/.." belowFile
let itemGroup = xdoc.SelectSingleNode(xpath)
let childXPath = sprintf "//Compile[@Include='%s']" belowFile
let aboveNode = itemGroup.SelectSingleNode(childXPath)
let node = createNewCompileNode xdoc newFileName
itemGroup.InsertAfter(node, aboveNode) |> ignore
xdoc.Save fsprojPath
Ok()

let addFile (fsprojPath: string) (newFileName: string) =
let xdoc = System.Xml.XmlDocument()
xdoc.Load fsprojPath
let newNode = createNewCompileNode xdoc newFileName

let compileItemGroups =
xdoc.SelectNodes("//Compile/.. | //None/.. | //EmbeddedResource/.. | //Content/..")
if fileAlreadyIncludedViaCompileTag xdoc newFileName then
Error "File already included in the project"
else
let newNode = createNewCompileNode xdoc newFileName

let hasExistingCompileElement = compileItemGroups.Count > 0
let compileItemGroups =
xdoc.SelectNodes("//Compile/.. | //None/.. | //EmbeddedResource/.. | //Content/..")

if hasExistingCompileElement then
let firstCompileItemGroup =
compileItemGroups |> Seq.cast<System.Xml.XmlNode> |> Seq.head
let hasExistingCompileElement = compileItemGroups.Count > 0

let x = firstCompileItemGroup.FirstChild
if hasExistingCompileElement then
let firstCompileItemGroup =
compileItemGroups |> Seq.cast<System.Xml.XmlNode> |> Seq.head

firstCompileItemGroup.InsertBefore(newNode, x) |> ignore
else
let itemGroup = xdoc.CreateElement("ItemGroup")
itemGroup.AppendChild(newNode) |> ignore
let projectNode = xdoc.SelectSingleNode("//Project")
projectNode.AppendChild(itemGroup) |> ignore
let x = firstCompileItemGroup.FirstChild

xdoc.Save fsprojPath
firstCompileItemGroup.InsertBefore(newNode, x) |> ignore
else
let itemGroup = xdoc.CreateElement("ItemGroup")
itemGroup.AppendChild(newNode) |> ignore
let projectNode = xdoc.SelectSingleNode("//Project")
projectNode.AppendChild(itemGroup) |> ignore

xdoc.Save fsprojPath
Ok()

let addExistingFile (fsprojPath: string) (existingFile: string) =
let relativePath =
Expand Down

0 comments on commit 5e28e81

Please sign in to comment.