public boolean delete()

in plugin/src/com/microsoft/alm/plugin/idea/tfvc/core/TFSFileSystemListener.java [65:225]


    public boolean delete(@NotNull final VirtualFile virtualFile) throws IOException {
        long startTime = System.nanoTime();
        ourLogger.trace("Delete command started for file " + virtualFile);

        final TFSVcs vcs = VcsHelper.getTFSVcsByPath(virtualFile);
        // no TFSVcs so not a TFVC project so do nothing
        if (vcs == null) {
            ourLogger.info("Not a TFVC project so not doing a TFVC delete");
            return false;
        }

        if (TFVCUtil.isInvalidTFVCPath(vcs, new LocalFilePath(virtualFile.getPath(), virtualFile.isDirectory()))) {
            ourLogger.warn("Invalid file name for TFVC, so not performing TFVC delete: {}", virtualFile.getPath());
            return false;
        }

        // do nothing with TFVC if the user chooses not to
        final VcsShowConfirmationOption.Value value = vcs.getDeleteConfirmation().getValue();
        if (VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY.equals(value)) {
            ourLogger.info("Don't delete file from TFVC: " + virtualFile.getPath());
            return false;
        }

        ourLogger.info("Deleting file with TFVC: " + virtualFile.getPath());
        final Project currentProject = vcs.getProject();

        // TODO: Currently, we cannot allow the users to undo directory deletion, because there's no non-recursive
        // `tf undo` command. In future, we may allow this after we drop legacy client support and add a undo command
        // that won't touch the disk at all.
        //
        // Currently, `tf undo` will always try to restore the whole directory (with ALL the files) which breaks the
        // undo behavior.
        if (virtualFile.isDirectory()) {
            ourLogger.info("Marking operation as non-undoable since directory \"{}\" is deleted", virtualFile);
            DocumentReference ref = DocumentReferenceManager.getInstance().create(virtualFile);
            UndoManager.getInstance(currentProject).nonundoableActionPerformed(ref, false);
        }

        TfvcClient tfvcClient = TfvcClient.getInstance();
        ServerContext serverContext = vcs.getServerContext(true);

        List<PendingChange> pendingChanges = tfvcClient.getStatusForFiles(
                currentProject,
                serverContext,
                Collections.singletonList(virtualFile.getPath()));

        // if 0 pending changes then just delete the file and return
        if (pendingChanges.isEmpty()) {
            ourLogger.info("No changes to file so deleting though TFVC");
            TfvcDeleteResult operationResult = tfvcClient.deleteFilesRecursively(
                    currentProject,
                    serverContext,
                    Collections.singletonList(TfsFileUtil.createLocalPath(virtualFile)));
            operationResult.throwIfErrorMessagesAreNotEmpty();

            long time = System.nanoTime() - startTime;
            ourLogger.trace("Delete command finished in " + time / 1_000_000_000.0 + "s");

            // There's a possibility that the path in question was explicitly ignored using .tfignore. In such case,
            // TFVC client will report that the file wasn't found and won't do anything with it. If this happened, then
            // we'll delegate the operation to IDEA (i.e. return false).
            List<TfsPath> notFoundPaths = operationResult.getNotFoundPaths();
            return notFoundPaths.isEmpty();
        }

        // start with assuming you don't need to revert but look at the pending changes to see if that's incorrect
        final AtomicBoolean revert = new AtomicBoolean(false);
        // assume false until we know we need to delete from TFVC
        final AtomicBoolean success = new AtomicBoolean(false);
        final AtomicBoolean isUndelete = new AtomicBoolean(false);
        for (final PendingChange pendingChange : pendingChanges) {
            StatusProvider.visitByStatus(new StatusVisitor() {
                public void scheduledForAddition(final @NotNull FilePath localPath,
                                                 final boolean localItemExists,
                                                 final @NotNull ServerStatus serverStatus) {
                    // revert the file and then let the IDE delete it
                    revert.set(true);
                    success.set(false);
                }

                public void unversioned(final @NotNull FilePath localPath,
                                        final boolean localItemExists,
                                        final @NotNull ServerStatus serverStatus) {
                    // only do something if it's an unversioned delete, the IDE will take care of it otherwise
                    if (pendingChange.getChangeTypes().contains(ServerStatusType.DELETE)) {
                        revert.set(true);
                        success.set(true);
                    }
                }

                public void scheduledForDeletion(final @NotNull FilePath localPath,
                                                 final boolean localItemExists,
                                                 final @NotNull ServerStatus serverStatus) {
                    // already deleted on server so let IDE take care of it
                    success.set(false);
                }

                public void checkedOutForEdit(final @NotNull FilePath localPath,
                                              final boolean localItemExists,
                                              final @NotNull ServerStatus serverStatus) {
                    // revert it and then delete it
                    revert.set(true);
                    success.set(true);
                }

                @Override
                public void locked(@NotNull FilePath localPath, boolean localItemExists, @NotNull ServerStatus serverStatus) {
                    // nothing to do if it's locked
                    success.set(false);
                }

                public void renamed(final @NotNull FilePath localPath, final boolean localItemExists, final @NotNull ServerStatus serverStatus) {
                    // revert it and then delete it
                    revert.set(true);
                    success.set(true);
                }

                public void renamedCheckedOut(final @NotNull FilePath localPath,
                                              final boolean localItemExists,
                                              final @NotNull ServerStatus serverStatus) {
                    // revert it and then delete it
                    revert.set(true);
                    success.set(true);
                }

                public void undeleted(final @NotNull FilePath localPath,
                                      final boolean localItemExists,
                                      final @NotNull ServerStatus serverStatus) {
                    // revert it and it will be deleted
                    revert.set(true);
                    isUndelete.set(true);
                }
            }, pendingChange);
        }

        if (revert.get()) {
            TfsPath filePath = TfsFileUtil.createLocalPath(virtualFile);
            tfvcClient.undoLocalChanges(currentProject, serverContext, Collections.singletonList(filePath));
        }

        if (success.get() && !isUndelete.get()) {
            ourLogger.info("Deleting file with TFVC after undoing pending changes");
            // PendingChanges will always have at least 1 element or else we wouldn't have gotten this far
            PendingChange pendingChange = pendingChanges.get(0);
            TfsPath itemToDelete = StringUtils.isNotEmpty(pendingChange.getSourceItem())
                    ? new TfsServerPath(pendingChange.getWorkspace(), pendingChange.getSourceItem())
                    : TfsFileUtil.createLocalPath(pendingChange.getLocalItem());
            TfvcDeleteResult operationResult = tfvcClient.deleteFilesRecursively(
                    currentProject,
                    serverContext,
                    Collections.singletonList(itemToDelete));
            operationResult.throwIfErrorMessagesAreNotEmpty();
            operationResult.throwIfNotFoundPathsAreNotEmpty();
        }
        ourLogger.info("File was deleted using TFVC: " + success.get());

        long time = System.nanoTime() - startTime;
        ourLogger.trace("Delete command finished in " + time / 1_000_000_000.0 + "s");

        return success.get();
    }