You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by "junichi11 (via GitHub)" <gi...@apache.org> on 2023/05/04 07:08:57 UTC

[GitHub] [netbeans] junichi11 commented on a diff in pull request #5900: [PHP] FixUsesPerformer and AddUseImportSuggestion improvement

junichi11 commented on code in PR #5900:
URL: https://github.com/apache/netbeans/pull/5900#discussion_r1184562897


##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -105,36 +110,56 @@ public FixUsesPerformer(
         this.options = options;
     }
 
-    public void perform() {
+    private void perform(boolean append) {
         final Document document = parserResult.getSnapshot().getSource().getDocument(false);
         if (document instanceof BaseDocument) {
             baseDocument = (BaseDocument) document;
             editList = new EditList(baseDocument);
             namespaceScope = ModelUtils.getNamespaceScope(parserResult.getModel().getFileScope(), importData.caretPosition);
             assert namespaceScope != null;
-            processSelections();
+            processSelections(append);
             editList.apply();
         }
     }
 
+    public void perform()
+    {

Review Comment:
   Formatting



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -425,28 +432,44 @@ private String createInsertString(final List<UsePart> useParts) {
         } else {
             Collections.sort(useParts);
         }
-        if (!useParts.isEmpty()) {
-            insertString.append(NEW_LINE);
-        }
+
         String indentString = null;
         if (options.preferGroupUses()
                 || options.preferMultipleUseStatementsCombined()) {
             CodeStyle codeStyle = CodeStyle.get(baseDocument);
             indentString = IndentUtils.createIndentString(codeStyle.getIndentSize(), codeStyle.expandTabToSpaces(), codeStyle.getTabSize());
         }
 
-        if (options.preferGroupUses()
+        usesInsertStringHelper.setWrapInNewLinesOnAppend(useParts.size() == 1);
+        if (options.preferGroupUses() && usesInsertStringHelper.canGroupUses()
                 && options.getPhpVersion().compareTo(PhpVersion.PHP_70) >= 0) {
-            insertString.append(createStringForGroupUse(useParts, indentString));
+            createStringForGroupUse(useParts, indentString, usesInsertStringHelper);
         } else if (options.preferMultipleUseStatementsCombined()) {
-            insertString.append(createStringForMultipleUse(useParts, indentString));
+            createStringForMultipleUse(useParts, indentString, usesInsertStringHelper);
         } else {
-            insertString.append(createStringForCommonUse(useParts));
+            createStringForCommonUse(useParts, usesInsertStringHelper);
+        }
+
+        if (!usesInsertStringHelper.isAppendUses()) {
+            try {
+                String replaceString = baseDocument.getText(usesInsertStringHelper.getInitialStartOffset(), usesInsertStringHelper.getStartOffset() - usesInsertStringHelper.getInitialStartOffset());
+                if (replaceString.equals(usesInsertStringHelper.getResultString())) {
+                    usesInsertStringHelper.resetResultString();
+                    return;
+                }
+            } catch (BadLocationException ex) {
+                Exceptions.printStackTrace(ex);

Review Comment:
   Maybe, I would add a log.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -501,7 +522,7 @@ private List<String> usePartsToNamespaces(List<UsePart> useParts) {
                 .collect(Collectors.toList());
     }
 
-    private void createStringForGroupUse(StringBuilder insertString, String indentString, String usePrefix, List<UsePart> useParts) {
+    private void createStringForGroupUse(String indentString, String usePrefix, List<UsePart> useParts, final UsesInsertStringHelper usesInsertStringHelper) {

Review Comment:
   I would add it to the first param. (Easy to see the diff.)



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -518,42 +539,56 @@ private void createStringForGroupUse(StringBuilder insertString, String indentSt
             if (groupUsePrefix != null) {
                 if (lastGroupUsePrefix != null
                         && !lastGroupUsePrefix.equals(groupUsePrefix)) {
-                    processGroupedUseParts(insertString, indentString, usePrefix, lastGroupUsePrefix, groupedUseParts);
+                    processGroupedUseParts(indentString, usePrefix, lastGroupUsePrefix, groupedUseParts, usesInsertStringHelper);
                 }
                 lastGroupUsePrefix = groupUsePrefix;
-                processNonGroupedUseParts(insertString, indentString, nonGroupedUseParts);
+                processNonGroupedUseParts(indentString, nonGroupedUseParts, usesInsertStringHelper);
                 groupedUseParts.add(usePart);
             } else {
-                processGroupedUseParts(insertString, indentString, usePrefix, lastGroupUsePrefix, groupedUseParts);
+                processGroupedUseParts(indentString, usePrefix, lastGroupUsePrefix, groupedUseParts, usesInsertStringHelper);
                 nonGroupedUseParts.add(usePart);
             }
         }
-        processNonGroupedUseParts(insertString, indentString, nonGroupedUseParts);
-        processGroupedUseParts(insertString, indentString, usePrefix, lastGroupUsePrefix, groupedUseParts);
+        processNonGroupedUseParts(indentString, nonGroupedUseParts, usesInsertStringHelper);
+        processGroupedUseParts(indentString, usePrefix, lastGroupUsePrefix, groupedUseParts, usesInsertStringHelper);
     }
 
-    private void processNonGroupedUseParts(StringBuilder insertString, String indentString, List<UsePart> nonGroupedUseParts) {
+    private void processNonGroupedUseParts(String indentString, List<UsePart> nonGroupedUseParts, final UsesInsertStringHelper usesInsertStringHelper) {
         if (nonGroupedUseParts.isEmpty()) {
             return;
         }
-        if (options.preferMultipleUseStatementsCombined()) {
-            insertString.append(createStringForMultipleUse(nonGroupedUseParts, indentString));
+        // it is hard to handle multiple use and group at once on appendUses
+        // because there is a probability of recursive changes, so we just fallback
+        // to common use insertion in such case to avoid complexity of code
+        if (options.preferMultipleUseStatementsCombined() && !usesInsertStringHelper.isAppendUses()) {
+            createStringForMultipleUse(nonGroupedUseParts, indentString, usesInsertStringHelper);
         } else {
-            insertString.append(createStringForCommonUse(nonGroupedUseParts));
+            createStringForCommonUse(nonGroupedUseParts, usesInsertStringHelper);
         }
         nonGroupedUseParts.clear();
     }
 
-    private void processGroupedUseParts(StringBuilder insertString, String indentString, String usePrefix, String groupUsePrefix, List<UsePart> groupedUseParts) {
+    private void processGroupedUseParts(String indentString, String usePrefix, String groupUsePrefix, List<UsePart> groupedUseParts, final UsesInsertStringHelper usesInsertStringHelper) {

Review Comment:
   I would add it to the first param. (Easy to see the diff.)



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -595,23 +666,78 @@ private String createStringForMultipleUse(List<UsePart> useParts, String indentS
             }
             insertString.append(usePart.getTextPart());
         }
-        if (!useParts.isEmpty()) {
-            insertString.append(SEMICOLON);
+        insertString.append(SEMICOLON);
+        if (usesInsertStringHelper.isAppendUses()) {
+            if (hasChanges) {
+                usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+            } else {
+                usesInsertStringHelper.resetPossibleRemoves();
+                usesInsertStringHelper.moveStartOffsetToTheEnd();
+            }
+        } else {
+            usesInsertStringHelper.appendToResultString(insertString.toString());
         }
-        return insertString.toString();
     }
 
-    private String createStringForCommonUse(List<UsePart> useParts) {
-        StringBuilder result = new StringBuilder();
+    private void createStringForCommonUse(List<UsePart> useParts, final UsesInsertStringHelper usesInsertStringHelper) {
+        StringBuilder insertString = new StringBuilder();
         UsePart.Type lastUseType = null;
+        boolean hasChanges = false;
+        int typeElementsCount = 0;
+        int firstElementOffset = 0;
         for (UsePart usePart : useParts) {
             if (putInPSR12Order && lastUseType != null && lastUseType != usePart.getType()) {
-                appendNewLine(result);
+                if (usesInsertStringHelper.isAppendUses()) {
+                    if (hasChanges) {
+                        usesInsertStringHelper.setWrapInNewLinesOnAppend(typeElementsCount == 1);
+                        usesInsertStringHelper.confirmResultStringAt(firstElementOffset);
+                    } else {
+                        usesInsertStringHelper.resetResultString();
+                        usesInsertStringHelper.moveStartOffsetToTheEnd();
+                        firstElementOffset = 0;
+                    }
+                    hasChanges = false;
+                } else {
+                    usesInsertStringHelper.appendToResultString(NEW_LINE);
+                }
+                typeElementsCount = 0;
             }
-            result.append(usePart.getUsePrefix()).append(usePart.getTextPart()).append(SEMICOLON);
+            // here we check changes in whole type block for PSR12Order
+            hasChanges = hasChanges || usePart.getOffset() == 0;
+            firstElementOffset = usePart.getOffset() == 0 || firstElementOffset != 0 && firstElementOffset < usePart.getOffset()
+                    ? firstElementOffset : usePart.getOffset();
             lastUseType = usePart.getType();
+            typeElementsCount++;
+
+            insertString.append(usePart.getUsePrefix()).append(usePart.getTextPart()).append(SEMICOLON);
+            if (usesInsertStringHelper.isAppendUses() && !putInPSR12Order) {
+                if (usesInsertStringHelper.moveEndOffset(usePart.getOffset())) {
+                    usesInsertStringHelper.addToPossibleRemoveMap(usePart.getOffset());
+                    usesInsertStringHelper.moveStartOffsetToTheEnd();
+                }
+                // here we check inline change only
+                hasChanges = usePart.getOffset() == 0;
+                if (hasChanges) {
+                    usesInsertStringHelper.applyDirectChange(insertString.toString());
+                } else {
+                    usesInsertStringHelper.resetPossibleRemoves();
+                }
+            } else {
+                usesInsertStringHelper.addToPossibleRemoveMap(usePart.getOffset());
+                usesInsertStringHelper.moveEndOffset(usePart.getOffset());
+                usesInsertStringHelper.appendToResultString(insertString.toString());
+            }
+            insertString = new StringBuilder();

Review Comment:
   The same as above.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {
+            return followingWhitespaceReplaceOffset;
+        }
+
+        public boolean needToSaveHeadingWhitespace(int offset) {
+            return savedHeadingWhitespaceOffset.contains(offset);
+        }
+
+        public boolean needToReplaceFollowingWhitespace(int offset) {
+            return followingWhitespaceReplaceOffset.contains(offset);
+        }
+
+        public void skipFollowingWhitespaceReplace(int offset) {
+            followingWhitespaceReplaceOffset.remove((Integer) offset);
+        }
+
+        public boolean moveEndOffset(int removedUseOffset) {
+            OffsetRange removed = this.getUsedRangeOffset(removedUseOffset);
+            if (removed != null) {
+                this.endOffset = removed.getEnd() > endOffset ? removed.getEnd() : endOffset;
+                return true;
+            }
+            return false;
+        }
+
+        public void moveStartOffsetToTheEnd() {

Review Comment:
   Nitpick: Can use shorter name (`moveStartOffsetToEnd`)



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {
+            return followingWhitespaceReplaceOffset;
+        }
+
+        public boolean needToSaveHeadingWhitespace(int offset) {
+            return savedHeadingWhitespaceOffset.contains(offset);
+        }
+
+        public boolean needToReplaceFollowingWhitespace(int offset) {
+            return followingWhitespaceReplaceOffset.contains(offset);
+        }
+
+        public void skipFollowingWhitespaceReplace(int offset) {
+            followingWhitespaceReplaceOffset.remove((Integer) offset);
+        }
+
+        public boolean moveEndOffset(int removedUseOffset) {
+            OffsetRange removed = this.getUsedRangeOffset(removedUseOffset);
+            if (removed != null) {
+                this.endOffset = removed.getEnd() > endOffset ? removed.getEnd() : endOffset;
+                return true;
+            }
+            return false;
+        }
+
+        public void moveStartOffsetToTheEnd() {
+            this.startOffset = this.endOffset;
+        }
+
+        public void applyDirectChangeAt(int offset, String insertString) {
+            if (offset == 0 || this.getUsedRangeOffset(offset) == null) {
+                applyDirectChange(insertString);
+                return;
+            }
+            int currentStart = this.startOffset;
+            this.startOffset = this.getUsedRangeOffset(offset).getEnd();
+            savedHeadingWhitespaceOffset.add(this.getUsedRangeOffset(offset).getStart());
+            // remove new line because of insert on existing place
+            insertString = insertString.substring(1);
+            applyDirectChange(insertString);
+            this.startOffset = currentStart;
+        }
+
+        public void applyDirectChange(String insertString)
+        {
+            for (Integer offset : usesToRemoveOffsetTmp) {
+                addToRemoveMap(offset);
+            }
+
+            if (shouldAppendLineBefore()) {
+                editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                followingWhitespaceReplaceOffset.add(getStartOffset());
+                editList.replace(getStartOffset(), 0, insertString, false, 0);
+            } else {
+                editList.replace(getStartOffset(), 0, insertString, false, 0);
+            }
+
+            if (wrapInNewLines) {
+                editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                wrapInNewLines = false;
+            }
+        }
+
+        public void setWrapInNewLinesOnAppend(boolean value) {
+            wrapInNewLines = value;
+        }
+
+        public void resetPossibleRemoves() {
+            usesToRemoveOffsetTmp.clear();
+        }
+
+        public void confirmResultStringAt(int offset) {
+            if (offset == 0 || this.getUsedRangeOffset(offset) == null) {
+                confirmResultString();
+                return;
+            }
+            int currentStart = this.startOffset;
+            this.startOffset = this.getUsedRangeOffset(offset).getEnd();
+            savedHeadingWhitespaceOffset.add(this.getUsedRangeOffset(offset).getStart());
+            // remove new line because of insert on existing place
+            this.resultString.deleteCharAt(0);
+            confirmResultString();
+            this.startOffset = currentStart;
+        }
+
+        public void confirmResultString() {
+            if (resultString.length() > 0) {
+                for (Integer offset : usesToRemoveOffsetTmp) {
+                    addToRemoveMap(offset);
+                }
+                if (appendUses && shouldAppendLineBefore()) {
+                    editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                    followingWhitespaceReplaceOffset.add(getStartOffset());
+                }
+                editList.replace(getStartOffset(), 0, resultString.toString(), false, 0);
+                if (appendUses && wrapInNewLines) {
+                    editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                    wrapInNewLines = false;
+                }
+                resultString = new StringBuilder();

Review Comment:
   The same as above.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -105,36 +110,56 @@ public FixUsesPerformer(
         this.options = options;
     }
 
-    public void perform() {
+    private void perform(boolean append) {
         final Document document = parserResult.getSnapshot().getSource().getDocument(false);
         if (document instanceof BaseDocument) {
             baseDocument = (BaseDocument) document;
             editList = new EditList(baseDocument);
             namespaceScope = ModelUtils.getNamespaceScope(parserResult.getModel().getFileScope(), importData.caretPosition);
             assert namespaceScope != null;
-            processSelections();
+            processSelections(append);
             editList.apply();
         }
     }
 
+    public void perform()
+    {
+        perform(false);
+    }
+
+    public void performAppend()
+    {

Review Comment:
   Formatting



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -595,23 +666,78 @@ private String createStringForMultipleUse(List<UsePart> useParts, String indentS
             }
             insertString.append(usePart.getTextPart());
         }
-        if (!useParts.isEmpty()) {
-            insertString.append(SEMICOLON);
+        insertString.append(SEMICOLON);
+        if (usesInsertStringHelper.isAppendUses()) {
+            if (hasChanges) {
+                usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+            } else {
+                usesInsertStringHelper.resetPossibleRemoves();
+                usesInsertStringHelper.moveStartOffsetToTheEnd();
+            }
+        } else {
+            usesInsertStringHelper.appendToResultString(insertString.toString());

Review Comment:
   The same as above.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -562,19 +597,55 @@ private void processGroupedUseParts(StringBuilder insertString, String indentStr
             insertString.append(indentString).append(groupUsePart.getTextPart().substring(prefixLength));
         }
         insertString.append(NEW_LINE).append(CURLY_CLOSE).append(SEMICOLON);
+        if (usesInsertStringHelper.isAppendUses()) {
+            if (hasChanges) {
+                usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+            } else {
+                usesInsertStringHelper.resetPossibleRemoves();
+                usesInsertStringHelper.moveStartOffsetToTheEnd();
+            }
+        } else {
+            usesInsertStringHelper.appendToResultString(insertString.toString());
+        }
         groupedUseParts.clear();
     }
 
-    private String createStringForMultipleUse(List<UsePart> useParts, String indentString) {
+    private void createStringForMultipleUse(List<UsePart> useParts, String indentString, final UsesInsertStringHelper usesInsertStringHelper) {
+        if (useParts.isEmpty()) {
+            return;
+        }
         StringBuilder insertString = new StringBuilder();
         UsePart.Type lastUsePartType = null;
+        boolean hasChanges = false;
+        int firstElementOffset = 0;
         for (Iterator<UsePart> it = useParts.iterator(); it.hasNext(); ) {
             UsePart usePart = it.next();
+            usesInsertStringHelper.addToPossibleRemoveMap(usePart.getOffset());
+            usesInsertStringHelper.moveEndOffset(usePart.getOffset());
+            if (usePart.getOffset() == 0) {
+                hasChanges = true;
+            } else {
+                firstElementOffset = usePart.getOffset() == 0 || firstElementOffset != 0 && firstElementOffset < usePart.getOffset()
+                    ? firstElementOffset : usePart.getOffset();
+            }
             if (lastUsePartType != null) {
                 if (lastUsePartType == usePart.getType()) {
                     insertString.append(COMMA).append(NEW_LINE).append(indentString);
                 } else {
                     insertString.append(SEMICOLON);
+                    if (usesInsertStringHelper.isAppendUses()) {
+                        if (hasChanges) {
+                            usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+                        } else {
+                            usesInsertStringHelper.resetPossibleRemoves();
+                            usesInsertStringHelper.moveStartOffsetToTheEnd();
+                            firstElementOffset = 0;
+                        }
+                    } else {
+                        usesInsertStringHelper.appendToResultString(insertString.toString());
+                    }

Review Comment:
   Maybe, we should be able to introduce the method because there is a similar code above.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -562,19 +597,55 @@ private void processGroupedUseParts(StringBuilder insertString, String indentStr
             insertString.append(indentString).append(groupUsePart.getTextPart().substring(prefixLength));
         }
         insertString.append(NEW_LINE).append(CURLY_CLOSE).append(SEMICOLON);
+        if (usesInsertStringHelper.isAppendUses()) {
+            if (hasChanges) {
+                usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+            } else {
+                usesInsertStringHelper.resetPossibleRemoves();
+                usesInsertStringHelper.moveStartOffsetToTheEnd();
+            }
+        } else {
+            usesInsertStringHelper.appendToResultString(insertString.toString());
+        }
         groupedUseParts.clear();
     }
 
-    private String createStringForMultipleUse(List<UsePart> useParts, String indentString) {
+    private void createStringForMultipleUse(List<UsePart> useParts, String indentString, final UsesInsertStringHelper usesInsertStringHelper) {
+        if (useParts.isEmpty()) {
+            return;
+        }
         StringBuilder insertString = new StringBuilder();
         UsePart.Type lastUsePartType = null;
+        boolean hasChanges = false;
+        int firstElementOffset = 0;
         for (Iterator<UsePart> it = useParts.iterator(); it.hasNext(); ) {
             UsePart usePart = it.next();
+            usesInsertStringHelper.addToPossibleRemoveMap(usePart.getOffset());
+            usesInsertStringHelper.moveEndOffset(usePart.getOffset());
+            if (usePart.getOffset() == 0) {
+                hasChanges = true;
+            } else {
+                firstElementOffset = usePart.getOffset() == 0 || firstElementOffset != 0 && firstElementOffset < usePart.getOffset()
+                    ? firstElementOffset : usePart.getOffset();
+            }
             if (lastUsePartType != null) {
                 if (lastUsePartType == usePart.getType()) {
                     insertString.append(COMMA).append(NEW_LINE).append(indentString);
                 } else {
                     insertString.append(SEMICOLON);
+                    if (usesInsertStringHelper.isAppendUses()) {
+                        if (hasChanges) {
+                            usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+                        } else {
+                            usesInsertStringHelper.resetPossibleRemoves();
+                            usesInsertStringHelper.moveStartOffsetToTheEnd();
+                            firstElementOffset = 0;
+                        }
+                    } else {
+                        usesInsertStringHelper.appendToResultString(insertString.toString());
+                    }
+                    hasChanges = false;
+                    insertString = new StringBuilder();

Review Comment:
   Please use`insertString.delete(0, insertString.length())` or 'insertString.setLength(0)'. Maybe, `new` is expensive.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {

Review Comment:
   Need check `ts.token() != null`, I suppose.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -518,42 +539,56 @@ private void createStringForGroupUse(StringBuilder insertString, String indentSt
             if (groupUsePrefix != null) {
                 if (lastGroupUsePrefix != null
                         && !lastGroupUsePrefix.equals(groupUsePrefix)) {
-                    processGroupedUseParts(insertString, indentString, usePrefix, lastGroupUsePrefix, groupedUseParts);
+                    processGroupedUseParts(indentString, usePrefix, lastGroupUsePrefix, groupedUseParts, usesInsertStringHelper);
                 }
                 lastGroupUsePrefix = groupUsePrefix;
-                processNonGroupedUseParts(insertString, indentString, nonGroupedUseParts);
+                processNonGroupedUseParts(indentString, nonGroupedUseParts, usesInsertStringHelper);
                 groupedUseParts.add(usePart);
             } else {
-                processGroupedUseParts(insertString, indentString, usePrefix, lastGroupUsePrefix, groupedUseParts);
+                processGroupedUseParts(indentString, usePrefix, lastGroupUsePrefix, groupedUseParts, usesInsertStringHelper);
                 nonGroupedUseParts.add(usePart);
             }
         }
-        processNonGroupedUseParts(insertString, indentString, nonGroupedUseParts);
-        processGroupedUseParts(insertString, indentString, usePrefix, lastGroupUsePrefix, groupedUseParts);
+        processNonGroupedUseParts(indentString, nonGroupedUseParts, usesInsertStringHelper);
+        processGroupedUseParts(indentString, usePrefix, lastGroupUsePrefix, groupedUseParts, usesInsertStringHelper);
     }
 
-    private void processNonGroupedUseParts(StringBuilder insertString, String indentString, List<UsePart> nonGroupedUseParts) {
+    private void processNonGroupedUseParts(String indentString, List<UsePart> nonGroupedUseParts, final UsesInsertStringHelper usesInsertStringHelper) {

Review Comment:
   I would add it to the first param. (Easy to see the diff.)



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {
+            return followingWhitespaceReplaceOffset;
+        }
+
+        public boolean needToSaveHeadingWhitespace(int offset) {
+            return savedHeadingWhitespaceOffset.contains(offset);
+        }
+
+        public boolean needToReplaceFollowingWhitespace(int offset) {
+            return followingWhitespaceReplaceOffset.contains(offset);
+        }
+
+        public void skipFollowingWhitespaceReplace(int offset) {
+            followingWhitespaceReplaceOffset.remove((Integer) offset);
+        }
+
+        public boolean moveEndOffset(int removedUseOffset) {
+            OffsetRange removed = this.getUsedRangeOffset(removedUseOffset);
+            if (removed != null) {
+                this.endOffset = removed.getEnd() > endOffset ? removed.getEnd() : endOffset;
+                return true;
+            }
+            return false;
+        }
+
+        public void moveStartOffsetToTheEnd() {
+            this.startOffset = this.endOffset;
+        }
+
+        public void applyDirectChangeAt(int offset, String insertString) {
+            if (offset == 0 || this.getUsedRangeOffset(offset) == null) {
+                applyDirectChange(insertString);
+                return;
+            }
+            int currentStart = this.startOffset;
+            this.startOffset = this.getUsedRangeOffset(offset).getEnd();
+            savedHeadingWhitespaceOffset.add(this.getUsedRangeOffset(offset).getStart());
+            // remove new line because of insert on existing place
+            insertString = insertString.substring(1);
+            applyDirectChange(insertString);
+            this.startOffset = currentStart;
+        }
+
+        public void applyDirectChange(String insertString)
+        {

Review Comment:
   Formatting



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {
+            return followingWhitespaceReplaceOffset;
+        }
+
+        public boolean needToSaveHeadingWhitespace(int offset) {
+            return savedHeadingWhitespaceOffset.contains(offset);
+        }
+
+        public boolean needToReplaceFollowingWhitespace(int offset) {
+            return followingWhitespaceReplaceOffset.contains(offset);
+        }
+
+        public void skipFollowingWhitespaceReplace(int offset) {
+            followingWhitespaceReplaceOffset.remove((Integer) offset);
+        }
+
+        public boolean moveEndOffset(int removedUseOffset) {
+            OffsetRange removed = this.getUsedRangeOffset(removedUseOffset);
+            if (removed != null) {
+                this.endOffset = removed.getEnd() > endOffset ? removed.getEnd() : endOffset;
+                return true;
+            }
+            return false;
+        }
+
+        public void moveStartOffsetToTheEnd() {
+            this.startOffset = this.endOffset;
+        }
+
+        public void applyDirectChangeAt(int offset, String insertString) {
+            if (offset == 0 || this.getUsedRangeOffset(offset) == null) {
+                applyDirectChange(insertString);
+                return;
+            }
+            int currentStart = this.startOffset;
+            this.startOffset = this.getUsedRangeOffset(offset).getEnd();
+            savedHeadingWhitespaceOffset.add(this.getUsedRangeOffset(offset).getStart());
+            // remove new line because of insert on existing place
+            insertString = insertString.substring(1);
+            applyDirectChange(insertString);

Review Comment:
   should not reassign it to the parameter.
   `applyDirectChange(insertString.substring(1));`



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -562,19 +597,55 @@ private void processGroupedUseParts(StringBuilder insertString, String indentStr
             insertString.append(indentString).append(groupUsePart.getTextPart().substring(prefixLength));
         }
         insertString.append(NEW_LINE).append(CURLY_CLOSE).append(SEMICOLON);
+        if (usesInsertStringHelper.isAppendUses()) {
+            if (hasChanges) {
+                usesInsertStringHelper.applyDirectChangeAt(firstElementOffset, insertString.toString());
+            } else {
+                usesInsertStringHelper.resetPossibleRemoves();
+                usesInsertStringHelper.moveStartOffsetToTheEnd();
+            }
+        } else {
+            usesInsertStringHelper.appendToResultString(insertString.toString());
+        }
         groupedUseParts.clear();
     }
 
-    private String createStringForMultipleUse(List<UsePart> useParts, String indentString) {
+    private void createStringForMultipleUse(List<UsePart> useParts, String indentString, final UsesInsertStringHelper usesInsertStringHelper) {
+        if (useParts.isEmpty()) {
+            return;
+        }
         StringBuilder insertString = new StringBuilder();
         UsePart.Type lastUsePartType = null;
+        boolean hasChanges = false;
+        int firstElementOffset = 0;
         for (Iterator<UsePart> it = useParts.iterator(); it.hasNext(); ) {
             UsePart usePart = it.next();
+            usesInsertStringHelper.addToPossibleRemoveMap(usePart.getOffset());
+            usesInsertStringHelper.moveEndOffset(usePart.getOffset());
+            if (usePart.getOffset() == 0) {
+                hasChanges = true;
+            } else {
+                firstElementOffset = usePart.getOffset() == 0 || firstElementOffset != 0 && firstElementOffset < usePart.getOffset()

Review Comment:
   Please add `()` to prevent misunderstanding.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;

Review Comment:
   `return Collections.unmodifiableMap(usesToRemove);`



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();

Review Comment:
   `final`



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {
+            return followingWhitespaceReplaceOffset;

Review Comment:
   `return Collections.unmodifiableSet(followingWhitespaceReplaceOffset);`



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {
+            return followingWhitespaceReplaceOffset;
+        }
+
+        public boolean needToSaveHeadingWhitespace(int offset) {
+            return savedHeadingWhitespaceOffset.contains(offset);
+        }
+
+        public boolean needToReplaceFollowingWhitespace(int offset) {
+            return followingWhitespaceReplaceOffset.contains(offset);
+        }
+
+        public void skipFollowingWhitespaceReplace(int offset) {
+            followingWhitespaceReplaceOffset.remove((Integer) offset);
+        }
+
+        public boolean moveEndOffset(int removedUseOffset) {
+            OffsetRange removed = this.getUsedRangeOffset(removedUseOffset);
+            if (removed != null) {
+                this.endOffset = removed.getEnd() > endOffset ? removed.getEnd() : endOffset;
+                return true;
+            }
+            return false;
+        }
+
+        public void moveStartOffsetToTheEnd() {
+            this.startOffset = this.endOffset;
+        }
+
+        public void applyDirectChangeAt(int offset, String insertString) {
+            if (offset == 0 || this.getUsedRangeOffset(offset) == null) {
+                applyDirectChange(insertString);
+                return;
+            }
+            int currentStart = this.startOffset;
+            this.startOffset = this.getUsedRangeOffset(offset).getEnd();
+            savedHeadingWhitespaceOffset.add(this.getUsedRangeOffset(offset).getStart());
+            // remove new line because of insert on existing place
+            insertString = insertString.substring(1);
+            applyDirectChange(insertString);
+            this.startOffset = currentStart;
+        }
+
+        public void applyDirectChange(String insertString)
+        {
+            for (Integer offset : usesToRemoveOffsetTmp) {
+                addToRemoveMap(offset);
+            }
+
+            if (shouldAppendLineBefore()) {
+                editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                followingWhitespaceReplaceOffset.add(getStartOffset());
+                editList.replace(getStartOffset(), 0, insertString, false, 0);
+            } else {
+                editList.replace(getStartOffset(), 0, insertString, false, 0);
+            }
+
+            if (wrapInNewLines) {
+                editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                wrapInNewLines = false;
+            }
+        }
+
+        public void setWrapInNewLinesOnAppend(boolean value) {
+            wrapInNewLines = value;
+        }
+
+        public void resetPossibleRemoves() {
+            usesToRemoveOffsetTmp.clear();
+        }
+
+        public void confirmResultStringAt(int offset) {
+            if (offset == 0 || this.getUsedRangeOffset(offset) == null) {
+                confirmResultString();
+                return;
+            }
+            int currentStart = this.startOffset;
+            this.startOffset = this.getUsedRangeOffset(offset).getEnd();
+            savedHeadingWhitespaceOffset.add(this.getUsedRangeOffset(offset).getStart());
+            // remove new line because of insert on existing place
+            this.resultString.deleteCharAt(0);
+            confirmResultString();
+            this.startOffset = currentStart;
+        }
+
+        public void confirmResultString() {
+            if (resultString.length() > 0) {
+                for (Integer offset : usesToRemoveOffsetTmp) {
+                    addToRemoveMap(offset);
+                }
+                if (appendUses && shouldAppendLineBefore()) {
+                    editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                    followingWhitespaceReplaceOffset.add(getStartOffset());
+                }
+                editList.replace(getStartOffset(), 0, resultString.toString(), false, 0);
+                if (appendUses && wrapInNewLines) {
+                    editList.replace(getStartOffset(), 0, NEW_LINE, false, 0);
+                    wrapInNewLines = false;
+                }
+                resultString = new StringBuilder();
+            }
+        }
+
+        public void resetResultString() {
+            resetPossibleRemoves();
+            resultString = new StringBuilder();

Review Comment:
   The same as above.



##########
php/php.editor/src/org/netbeans/modules/php/editor/actions/FixUsesPerformer.java:
##########
@@ -631,13 +757,221 @@ private int getOffsetWithoutLeadingWhitespaces(final int startOffset) {
         return result;
     }
 
+    private int getOffsetWithoutFollowingWhitespaces(final int endOffset) {
+        int result = endOffset;
+        baseDocument.readLock();
+        try {
+            TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(baseDocument, endOffset);
+            if (ts != null) {
+                ts.move(endOffset);
+                while (ts.moveNext() && ts.token().id().equals(PHPTokenId.WHITESPACE)) {
+                    result = ts.offset() + ts.token().length();
+                }
+            }
+        } finally {
+            baseDocument.readUnlock();
+        }
+        return result;
+    }
+
+    private class UsesInsertStringHelper {
+        private final Map<Integer, OffsetRange> usedRanges;
+        private final boolean appendUses;
+        private final boolean hasMultipleUses;
+        private StringBuilder resultString = new StringBuilder();
+        private Set<Integer> followingWhitespaceReplaceOffset = new HashSet<>();
+        private Set<Integer> savedHeadingWhitespaceOffset = new HashSet<>();
+        private int startOffset;
+        private int initialStartOffset;
+        private int endOffset;
+        private boolean wrapInNewLines = false;
+        private Map<Integer, Integer> usesToRemove = new HashMap<>();
+        private List<Integer> usesToRemoveOffsetTmp = new ArrayList<>();
+
+        UsesInsertStringHelper(Map<Integer, OffsetRange> usedRanges, boolean hasMultipleUses, boolean appendUses, int startOffset) {
+            this.usedRanges = usedRanges;
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.hasMultipleUses = hasMultipleUses;
+            this.appendUses = appendUses;
+        }
+
+        public OffsetRange getUsedRangeOffset(int offset) {
+            return usedRanges.get(offset);
+        }
+
+        public void addToPossibleRemoveMap(int offset) {
+            if (usedRanges.get(offset) != null) {
+                usesToRemoveOffsetTmp.add(offset);
+            }
+        }
+
+        public Map<Integer, Integer> getUsesToRemove() {
+            return usesToRemove;
+        }
+
+        public int getStartOffset() {
+            return startOffset;
+        }
+
+        public int getInitialStartOffset() {
+            return initialStartOffset;
+        }
+
+        public void namespaceChange(int startOffset) {
+            this.startOffset = startOffset;
+            this.initialStartOffset = startOffset;
+            this.endOffset = startOffset;
+        }
+
+        public java.util.Set<Integer> getFollowingWhitespaceReplaceOffsets() {

Review Comment:
   `public Set<Integer> getFollowingWhitespaceReplaceOffsets() {`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists