diff --git a/pdt.refactoring/META-INF/MANIFEST.MF b/pdt.refactoring/META-INF/MANIFEST.MF index 06a11d556..1cbfd933b 100644 --- a/pdt.refactoring/META-INF/MANIFEST.MF +++ b/pdt.refactoring/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: PDT Refactoring -Bundle-SymbolicName: org.cs3.pdt.refactoring +Bundle-SymbolicName: org.cs3.pdt.refactoring;singleton:=true Bundle-Version: 3.0.0.qualifier Bundle-Activator: org.cs3.pdt.refactoring.PDTRefactoringPlugin Require-Bundle: org.eclipse.ui, @@ -10,6 +10,15 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ltk.core.refactoring, org.eclipse.ltk.ui.refactoring, org.cs3.pdt.common;bundle-version="[3.0.0,4.0.0)", - org.eclipse.jface.text + org.eclipse.jface.text, + org.eclipse.ui.workbench, + org.cs3.pdt.editor;bundle-version="3.0.0", + org.eclipse.ui.workbench.texteditor;bundle-version="3.8.101", + org.eclipse.ui.editors Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy +Import-Package: org.cs3.jtransformer.util, + org.cs3.pdt.editor.internal.editors, + org.eclipse.jdt.ui, + org.eclipse.ui, + org.eclipse.ui.editors.text diff --git a/pdt.refactoring/build.properties b/pdt.refactoring/build.properties index 34d2e4d2d..e9863e281 100644 --- a/pdt.refactoring/build.properties +++ b/pdt.refactoring/build.properties @@ -1,4 +1,5 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ - . + .,\ + plugin.xml diff --git a/pdt.refactoring/doc/todo.txt b/pdt.refactoring/doc/todo.txt new file mode 100644 index 000000000..42d5bdff5 --- /dev/null +++ b/pdt.refactoring/doc/todo.txt @@ -0,0 +1,3 @@ +- automatically consult the files in the pl folder +- Change "Refactoring" to "Renaming" and think about a concept to include various refacorings +- Create Test suites diff --git a/pdt.refactoring/pl/SamplePrologFile.pl b/pdt.refactoring/pl/SamplePrologFile.pl new file mode 100644 index 000000000..6ed787c4e --- /dev/null +++ b/pdt.refactoring/pl/SamplePrologFile.pl @@ -0,0 +1,2 @@ +isCity(bonn). +isCity(copenhagen). \ No newline at end of file diff --git a/pdt.refactoring/pl/load.pl b/pdt.refactoring/pl/load.pl new file mode 100644 index 000000000..76338830f --- /dev/null +++ b/pdt.refactoring/pl/load.pl @@ -0,0 +1,4 @@ +:- use_module('prolog_refactoring_demo_renaming', + [ rename_predicate/6 % (+M:N/A, +NewName, ?File, ?Start, ?End, ?NewText) + ] + ). \ No newline at end of file diff --git a/pdt.refactoring/pl/prolog_refactoring_demo_renaming.pl b/pdt.refactoring/pl/prolog_refactoring_demo_renaming.pl new file mode 100644 index 000000000..1441b91c4 --- /dev/null +++ b/pdt.refactoring/pl/prolog_refactoring_demo_renaming.pl @@ -0,0 +1,58 @@ +:- module(prolog_refactoring_demo_renaming, [ + rename_predicate/6 % (+M:N/A, +NewName, ?File, ?Start, ?End, ?NewText) +]). + +:- use_module(library(prolog_clause)). +:- use_module(pdt_common_pl('callgraph/pdt_call_graph')). + +:- dynamic(result/4). + +assert_result(Goal, Caller, From, CallKind) :- + assertz(result(Goal, Caller, From, CallKind)). + +%% rename_predicate(+PI, +NewName, File, Start, End, NewText) +% +% Rename M:N/A replacing N by NewName. +% File, Start, End and NewText form a Text Change. +% In the case of a predicate renaming Length is the Length of N +% and NewText = NewName in all TextChange values. + +rename_predicate(M:N/A, NewName, File, Start, End, NewText) :- + NewText = NewName, + position_of(M:N/A, File, Start, End). + +% We need all declarations (predicate heads) and calls of M:N/A. +% F is the file and S is the start position of N in the file: +position_of(M:N/A, File, Start, End) :- declaration_positon(M:N/A, File, Start, End). +position_of(M:N/A, File, Start, End) :- call_positon( M:N/A, File, Start, End). + + +declaration_positon(M:N/A, File, Start, End) :- + functor(Head, N, A), + clause(M:Head, _, Ref), + clause_info(Ref, File, TermPosition, _), + ( clause_property(Ref, fact) + -> % fact + TermPosition = HeadPosition + ; % clause with body + TermPosition = term_position(_, _, _, _, [HeadPosition|_]) + ), + ( HeadPosition = Start-End + -> % no arguments + true + ; % at least one argument + HeadPosition = term_position(_, _, Start, End, _) + ). + +call_positon(M:N/A, File, Start, End) :- + retractall(result(_, _, _, _)), + functor(Head, N, A), + pdt_walk_code([trace_reference(M:Head), on_trace(prolog_refactoring_demo_renaming:assert_result)]), + !, + retract(result(_Goal, _Caller, From, _CallKind)), + From = clause_term_position(Ref, TermPosition), + clause_property(Ref, file(File)), + ( TermPosition = Start-End + -> true + ; TermPosition = term_position(_, _, Start, End, _) + ). diff --git a/pdt.refactoring/plugin.xml b/pdt.refactoring/plugin.xml new file mode 100644 index 000000000..099a97b9c --- /dev/null +++ b/pdt.refactoring/plugin.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoring.java b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoring.java index 2eaa42f8e..e183e6f5d 100644 --- a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoring.java +++ b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoring.java @@ -1,27 +1,164 @@ package org.cs3.pdt.refactoring; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.cs3.pdt.common.PDTCommonUtil; +import org.cs3.pdt.common.metadata.Goal; +import org.cs3.pdt.connector.util.FileUtils; +import org.cs3.pdt.editor.internal.editors.PLEditor; +import org.cs3.prolog.connector.common.Debug; +import org.cs3.prolog.connector.common.QueryUtils; import org.cs3.prolog.connector.process.PrologProcess; -import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring; +import org.cs3.prolog.connector.process.PrologProcessException; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.CompositeChange; +import org.eclipse.ltk.core.refactoring.Refactoring; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; -import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Display; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.ui.handlers.HandlerUtil; + -public class PDTRefactoring { +public class PDTRefactoring extends Refactoring{ - public static final String VARIABLE_REPLACEMENT = "Replacement"; - public static final String VARIABLE_OFFSET_END = "OffsetEnd"; - public static final String VARIABLE_OFFSET_START = "OffsetStart"; + public static final String VARIABLE_NEW_TEXT = "NewText"; + public static final String VARIABLE_OFFSET_END = "End"; + public static final String VARIABLE_OFFSET_START = "Start"; public static final String VARIABLE_FILE = "File"; + + public static final String REFACTORING_NAME = "PDT Refactoring"; - public static int runRefactoringWithQuery(Shell shell, String query) throws InterruptedException { - return runRefactoringWithQuery(shell, PDTCommonUtil.getActivePrologProcess(), query); - } + private PDTRefactoringWizard wizard; + private PrologProcess prologProcess; + private PLEditor editor; + + private Goal goal; - public static int runRefactoringWithQuery(Shell shell, PrologProcess process, String query) throws InterruptedException { - ProcessorBasedRefactoring refactoring = new ProcessorBasedRefactoring(new PDTRefactoringProcessor(process, query)); - PDTRefactoringWizard wizard = new PDTRefactoringWizard(refactoring); + public void init(ExecutionEvent event){ + + editor = (PLEditor)HandlerUtil.getActiveEditor(event); + + // Get a reference to the running prolog process + prologProcess = PDTCommonUtil.getActivePrologProcess(); + //TODO this should be done asyncronously and I need a method be be sure it finished + try { + prologProcess.start(); + } catch (PrologProcessException e1) { + e1.printStackTrace(); + } + + // Create the Refactoring wizard... + wizard = new PDTRefactoringWizard(this); + + // Start the wizard! RefactoringWizardOpenOperation operation = new RefactoringWizardOpenOperation(wizard); - return operation.run(shell, "PDT Refactoring"); + + try { + operation.run(HandlerUtil.getActiveShell(event), this.getName()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + + + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) + throws CoreException, OperationCanceledException { + + return new RefactoringStatus(); } + @Override + public RefactoringStatus checkFinalConditions(IProgressMonitor pm) + throws CoreException, OperationCanceledException { + + return new RefactoringStatus(); + } + + + + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { + String newText = wizard.getNewText(); + // The prolog predicate that is to be renamed + + Display.getDefault().syncExec(new Runnable() { + public void run() { + try { + goal = editor.getSelectedPrologElement(); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + }); + + + String query = QueryUtils.bT("rename_predicate", "user:"+goal.toString(),"'"+newText+"'", + VARIABLE_FILE,VARIABLE_OFFSET_START,VARIABLE_OFFSET_END,VARIABLE_NEW_TEXT); + + + + CompositeChange change = null; + try { + List> results = prologProcess.queryAll(query); + System.out.println(results); + change = new CompositeChange("Replacements"); + HashMap changes = new HashMap<>(); + for (Map result : results) { + try { + //TODO Check for exceptional results + //TODO instanceof check + String pathOut = (String) result.get(VARIABLE_FILE); + int startOut = Integer.parseInt((String) result.get(VARIABLE_OFFSET_START)); + int endOut = Integer.parseInt((String) result.get(VARIABLE_OFFSET_END)); + String replacementOut = (String) result.get(VARIABLE_NEW_TEXT); + int offset = startOut; + int length = endOut-startOut; + + + //path = Util.unquoteAtom(path); TODO: What do I need this for? + TextFileChange textFileChange = changes.get(pathOut); + if (textFileChange == null) { + //IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile( + // new Path(pathOut).makeAbsolute()); + IFile file = FileUtils.findFileForLocation(pathOut); + textFileChange = new TextFileChange(file.getName(), file); + MultiTextEdit fileChangeRootEdit = new MultiTextEdit(); + textFileChange.setEdit(fileChangeRootEdit); + changes.put(pathOut, textFileChange); + change.add(textFileChange); + + } + + ReplaceEdit replaceEdit = new ReplaceEdit(offset, length, replacementOut); + textFileChange.addEdit(replaceEdit); + } catch (Exception e) { + Debug.report(e); + } + } + } catch (PrologProcessException e) { + Debug.report(e); + } + return change; + } + + @Override + public String getName() { + return REFACTORING_NAME; + } + + } diff --git a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringHandler.java b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringHandler.java new file mode 100644 index 000000000..29214dede --- /dev/null +++ b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringHandler.java @@ -0,0 +1,17 @@ +package org.cs3.pdt.refactoring; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; + +public class PDTRefactoringHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + + new PDTRefactoring().init(event); + + return null; + } + +} diff --git a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringProcessor.java b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringProcessor.java deleted file mode 100644 index eb60e37ae..000000000 --- a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringProcessor.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.cs3.pdt.refactoring; - -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.cs3.pdt.connector.util.FileUtils; -import org.cs3.pdt.connector.util.UIUtils; -import org.cs3.prolog.connector.common.Debug; -import org.cs3.prolog.connector.common.Util; -import org.cs3.prolog.connector.process.PrologProcess; -import org.cs3.prolog.connector.process.PrologProcessException; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.ltk.core.refactoring.Change; -import org.eclipse.ltk.core.refactoring.CompositeChange; -import org.eclipse.ltk.core.refactoring.RefactoringStatus; -import org.eclipse.ltk.core.refactoring.TextFileChange; -import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; -import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; -import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor; -import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; - -public class PDTRefactoringProcessor extends RefactoringProcessor { - - private static final String PROCESSOR_ID = "org.cs3.pdt.refactoring.processor"; - private static final String PROCESSOR_NAME = "PDT Refactoring Processor"; - - private final PrologProcess process; - private final String query; - - public PDTRefactoringProcessor(PrologProcess process, String query) { - this.process = process; - this.query = query; - } - - @Override - public Object[] getElements() { - return new Object[]{""}; - } - - @Override - public String getIdentifier() { - return PROCESSOR_ID; - } - - @Override - public String getProcessorName() { - return PROCESSOR_NAME; - } - - @Override - public boolean isApplicable() throws CoreException { - return true; - } - - @Override - public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { - return new RefactoringStatus(); - } - - @Override - public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { - return new RefactoringStatus(); - } - - @Override - public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { - return new RefactoringParticipant[0]; - } - - @Override - public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { - Change change = null; - try { - List> results = process.queryAll(query); - change = new CompositeChange("Replacements"); - HashMap changes = new HashMap<>(); - for (Map result : results) { - try { - String path = (String) result.get(PDTRefactoring.VARIABLE_FILE); - int offsetStart = Integer.parseInt((String) result.get(PDTRefactoring.VARIABLE_OFFSET_START)); - int offsetEnd = Integer.parseInt((String) result.get(PDTRefactoring.VARIABLE_OFFSET_END)); - String replacement = (String) result.get(PDTRefactoring.VARIABLE_REPLACEMENT); - if (path == null || replacement == null) { - continue; - } - path = Util.unquoteAtom(path); - TextFileChange textFileChange = changes.get(path); - if (textFileChange == null) { - IFile file = FileUtils.findFileForLocation(path); - textFileChange = new TextFileChange(file.getName(), file); - MultiTextEdit fileChangeRootEdit = new MultiTextEdit(); - textFileChange.setEdit(fileChangeRootEdit); - changes.put(path, textFileChange); - } - - IDocument document = UIUtils.getDocument(new File(path)); - int convertedOffsetStart = UIUtils.logicalToPhysicalOffset(document, offsetStart); - int convertedOffsetEnd = UIUtils.logicalToPhysicalOffset(document, offsetEnd); - ReplaceEdit replaceEdit = new ReplaceEdit(convertedOffsetStart, convertedOffsetEnd - convertedOffsetStart, replacement); - textFileChange.addEdit(replaceEdit); - } catch (Exception e) { - Debug.report(e); - } - } - } catch (PrologProcessException e) { - Debug.report(e); - } - return change; - } - -} diff --git a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringWizard.java b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringWizard.java index f48f05178..9abe20042 100644 --- a/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringWizard.java +++ b/pdt.refactoring/src/org/cs3/pdt/refactoring/PDTRefactoringWizard.java @@ -5,13 +5,31 @@ public class PDTRefactoringWizard extends RefactoringWizard { + + + private RenameWizardPage renamePage; + private String newText; + public PDTRefactoringWizard(Refactoring refactoring) { super(refactoring, WIZARD_BASED_USER_INTERFACE); setForcePreviewReview(true); + this.renamePage = new RenameWizardPage(); } @Override protected void addUserInputPages() { + addPage(renamePage); + } + + public String getNewText() { + return this.newText; + } + + public void setNewText(String text) { + this.newText = text; + } + + } diff --git a/pdt.refactoring/src/org/cs3/pdt/refactoring/RenameWizardPage.java b/pdt.refactoring/src/org/cs3/pdt/refactoring/RenameWizardPage.java new file mode 100644 index 000000000..8a74520d9 --- /dev/null +++ b/pdt.refactoring/src/org/cs3/pdt/refactoring/RenameWizardPage.java @@ -0,0 +1,66 @@ +package org.cs3.pdt.refactoring; + +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +public class RenameWizardPage extends UserInputWizardPage { + + public final static String NAME = "Rename Prolog Element"; + private Text fNameField; + + + public RenameWizardPage() { + super(NAME); + } + + @Override + public void createControl(Composite parent) { + Composite composite= new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + composite.setFont(parent.getFont()); + + Label label= new Label(composite, SWT.NONE); + label.setText("Enter new name"); + label.setLayoutData(new GridData()); + + fNameField= new Text(composite, SWT.BORDER); + fNameField.setFont(composite.getFont()); + fNameField.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false)); + fNameField.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validatePage(); + } + }); + + + + + //fNameField.selectAll(); + setPageComplete(false); + setControl(composite); + } + + private void validatePage() { + String text= fNameField.getText(); + boolean status; + if(text == null || text.equalsIgnoreCase("")) + status = false; + else + status = true; + ((PDTRefactoringWizard)getWizard()).setNewText(text); + setPageComplete(status); + } + + public String getNewName() { + return fNameField.getText(); + } + +}