ReplaceSpacesWithTabsQuickFix.java
/*******************************************************************************
* Copyright (c) 2021 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.jdt.internal.corext.fix.helper;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.IMarkerResolution;
/**
* Quick fix to replace leading 4 spaces with tabs in XML files.
* Only replaces spaces at the beginning of lines, not inside text content.
*/
public class ReplaceSpacesWithTabsQuickFix implements IMarkerResolution {
private static final ILog LOG = Platform.getLog(ReplaceSpacesWithTabsQuickFix.class);
private static final String PLUGIN_ID = "org.sandbox.jdt.internal.corext.fix.helper";
@Override
public String getLabel() {
return "Replace leading 4 spaces with tabs";
}
@Override
public void run(IMarker marker) {
try {
IResource resource = marker.getResource();
if (!(resource instanceof IFile)) {
LOG.log(new Status(IStatus.WARNING, PLUGIN_ID,
"Marker resource is not a file: " + resource));
return;
}
IFile file = (IFile) resource;
// Read file content
String content;
try (InputStream is = file.getContents()) {
content = new String(is.readAllBytes(), StandardCharsets.UTF_8);
}
// Detect original line ending style
String lineEnding = "\n";
if (content.contains("\r\n")) {
lineEnding = "\r\n";
}
// Replace leading 4 spaces with tabs (line by line to avoid replacing inline spaces)
String[] lines = content.split("\r?\n", -1); // -1 to preserve empty lines
StringBuilder result = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
// Count leading spaces
int leadingSpaces = 0;
while (leadingSpaces < line.length() && line.charAt(leadingSpaces) == ' ') {
leadingSpaces++;
}
// Convert groups of 4 leading spaces to tabs
int numTabs = leadingSpaces / 4;
int remainingSpaces = leadingSpaces % 4;
// Rebuild line with tabs
if (numTabs > 0) {
result.append("\t".repeat(numTabs));
}
if (remainingSpaces > 0) {
result.append(" ".repeat(remainingSpaces));
}
result.append(line.substring(leadingSpaces));
if (i < lines.length - 1) {
result.append(lineEnding);
}
}
// Write back using Eclipse workspace API
byte[] newContent = result.toString().getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream inputStream = new ByteArrayInputStream(newContent);
file.setContents(inputStream, IResource.KEEP_HISTORY, null);
// Refresh resource
file.refreshLocal(IResource.DEPTH_ZERO, null);
// Delete marker
marker.delete();
LOG.log(new Status(IStatus.INFO, PLUGIN_ID,
"Replaced leading spaces with tabs in: " + file.getName()));
} catch (CoreException e) {
LOG.log(new Status(IStatus.ERROR, PLUGIN_ID,
"Error applying quick fix", e));
} catch (Exception e) {
LOG.log(new Status(IStatus.ERROR, PLUGIN_ID,
"Unexpected error applying quick fix", e));
}
}
}