SandboxHintRenameParticipant.java

/*******************************************************************************
 * Copyright (c) 2026 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 - initial API and implementation
 *******************************************************************************/
package org.sandbox.jdt.triggerpattern.editor;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;

/**
 * Rename participant for {@code .sandbox-hint} files.
 *
 * <p>When a method in a {@code <? ?>} block is renamed, all
 * {@code :: guardName()} references in the same file are updated.</p>
 *
 * <p>Implemented as {@link IDocument}-based text search (no AST needed):
 * finds all occurrences of {@code :: oldName(} in the default content type
 * and replaces them with {@code :: newName(}.</p>
 *
 * @since 1.5.0
 */
public class SandboxHintRenameParticipant {

	/**
	 * Computes text edits for renaming a guard function reference.
	 *
	 * <p>Finds all occurrences of {@code :: oldName(} in the document
	 * and replaces them with {@code :: newName(}. References in comments
	 * (lines starting with {@code //}) are not changed.</p>
	 *
	 * @param document the document to search
	 * @param oldName  the old guard function name
	 * @param newName  the new guard function name
	 * @return a {@link TextEdit} with all replacements, or {@code null} if no changes needed
	 */
	public TextEdit computeRenameEdits(IDocument document, String oldName, String newName) {
		if (document == null || oldName == null || newName == null || oldName.equals(newName)) {
			return null;
		}

		String content;
		try {
			content = document.get();
		} catch (RuntimeException e) {
			return null;
		}

		MultiTextEdit multiEdit = new MultiTextEdit();

		// Pattern: "::" followed by optional whitespace, then oldName followed by "("
		String regex = "::\\s*" + Pattern.quote(oldName) + "\\("; //$NON-NLS-1$ //$NON-NLS-2$
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(content);

		while (matcher.find()) {
			// Check if the line is a comment
			try {
				int lineNum = document.getLineOfOffset(matcher.start());
				int lineOffset = document.getLineOffset(lineNum);
				String linePrefix = content.substring(lineOffset, matcher.start()).trim();
				if (linePrefix.startsWith("//")) { //$NON-NLS-1$
					continue; // Skip comment lines
				}
			} catch (BadLocationException e) {
				continue;
			}

			// Replace the matched portion: ":: oldName(" → ":: newName("
			String matched = matcher.group();
			String replacement = matched.replace(oldName, newName);
			multiEdit.addChild(new ReplaceEdit(matcher.start(), matched.length(), replacement));
		}

		if (!multiEdit.hasChildren()) {
			return null;
		}
		return multiEdit;
	}
}