How to use rename refactoring as part of a quickfix? - xtext

I've added a quickfix option to my DSL in which I want to make some modifications to the document text - including renaming some element. I can change text in that element just fine, but I want to also rename all of its references - i.e. rename refactoring. How do I do that?
Can I somehow trigger the built-in rename refactoring from inside a quickfix? Or alternatively, how do I go over all of the element's references and change them?

So, I found a way to programmatically trigger a rename refactor. I don't know if it's the "proper" way - I guess it isn't, since I had to add #SuppressWarnings("restriction") to my code - but it works:
private void performDirectRenameRefactoring(EObject object, String newName) throws InterruptedException {
XtextEditor editor = EditorUtils.getActiveXtextEditor();
IRenameElementContext renameContext = new IRenameElementContext.Impl(
EcoreUtil.getURI(object),
object.eClass(),
editor,
editor.getSelectionProvider().getSelection(),
null);
IRenameSupport rename = renameSupportFactory.create(renameContext, newName);
rename.startDirectRefactoring();
}
So to call this from a quick fix, all you need to do is to get the EObject and calculate the new name. If the issue occupies a part of the EObject itself, the object could be retrieved by:
private EObject findObject(IXtextDocument doc, final Issue issue) {
EObject object = doc.readOnly(new IUnitOfWork<EObject, XtextResource>() {
public EObject exec(XtextResource state) throws Exception {
return state.getEObject(issue.getUriToProblem().fragment());
}
});
}
You can get an IXtextDocument from either IssueResolutionAcceptor (which you should have if you're handling an issue) or from IModificationContext (which you should have if you're proposing a change).

Oak, thank you very much for the solution. Here is my version in Xtend.
#Inject(optional=true)
IRenameSupport.Factory renameSupportFactory;
#Inject(optional=true)
IRenameContextFactory renameContextFactory;
#Fix(VhdlValidator::INVALID_SIGNAL_NAME_ENDING)
def addSignalEnding(Issue issue, IssueResolutionAcceptor acceptor) {
acceptor.accept(issue, 'Add the "_s" ending', 'Add the "_s" ending.', 'upcase.png') [
EObject element, IModificationContext context |
val editor = EditorUtils.getActiveXtextEditor();
val renameElementContext = editor.getDocument().readOnly(
new IUnitOfWork<IRenameElementContext, XtextResource>()
{
override def IRenameElementContext exec(XtextResource state)
{
renameContextFactory.createRenameElementContext(element,
editor, null, state);
}
});
val rename = renameSupportFactory.create(renameElementContext, (element as Signal).name + "_s" );
rename.startDirectRefactoring();
]
}

Related

Xtext CustomScopeProvider Problems with adding candidates

I am working on an xtext Project where I have to customize the Scope Provider. I need to add up some possible candidates for the scope. The first part (getServiceInputs()) works fine but for the second one (addAll(sub.GetSubRecipeParameters()) not. Debugging showed that they get removed from its original source (sub) and can therefore not be retrieved again. When simply commenting out the addAll line the SubRecipeParameters remain in sub. Really dont know how to solve that, tried already some work arounds. Anyone with an Idea?
public class AutomationServiceDslScopeProvider extends AbstractAutomationServiceDslScopeProvider {
#Override
public IScope getScope(EObject context, EReference reference) {
if (context instanceof ServiceInvocationParameter
&& reference == AutomationServiceDslPackage.Literals.LITERAL) {
ServiceInvocation serviceCall = (ServiceInvocation) invocationParameter.eContainer();
ServiceDefinition calledService = serviceCall.getService();
List<ServiceParameterDefinition> candidates= calledService.getServiceInputs();
final EObject rootContainer = EcoreUtil.getRootContainer(context);
List<SubRecipeDefinition> subs = EcoreUtil2.getAllContentsOfType(rootContainer, SubRecipeDefinition.class);
for(SubRecipeDefinition sub:subs) {
for(RecipeStep step:sub.getRecipeSteps()) {
if(step.getName()==serviceCall.getName()) {
candidates.addAll(sub.getSubRecipeParameters());
}
}
}
return Scopes.scopeFor(candidates);
Thanks for any help!!
This is normal EMF behaviour if you move elements from one EList to another one. The solution is to create a new list e.g. new ArrayList<>() and also add the inputs there
List<ServiceParameterDefinition> candidates = new ArrayList<>();
candidates.addAll(calledService.getServiceInputs());

Syntax issue with then method on Navigator

I have the following code snippet:
_openEditListingDialog(Listing listing,
Function(Listing) onSubmittedCallback) async {
Navigator
.of(context)
.push(
new MaterialPageRoute<Listing>(
builder: (BuildContext context) {
return new ListingDialog.edit(listing);
},
fullscreenDialog: true,
),
)
.then((Listing newEntry) {
if (newEntry != null) {
newEntry.id = listing.id;
onSubmittedCallback(newEntry);
}
});
}
VSCode complains on the .then line with the following error:
[dart] The argument type '(Listing) -> Null' can't be assigned to the parameter type '(Object) -> FutureOr<Null>'.
What does this mean and how do I correct this? I am new to dart and flutter and error messages like this are cryptic for me. My code is based on this example: https://github.com/MSzalek-Mobile/weight_tracker/blob/v0.4.1/lib/home_page.dart.
TBH I'm not sure why VS Code is flagging that as an error for you - I see nothing substantially wrong with it and neither does my VS Code or IntelliJ. I pasted your code in and after adding a Listing class, I had no problem at all (for the record, if you paste enough code that someone can actually run things when you have a problem it makes it a lot easier for them to help you).
And in this particular case, the line number for the error might be quite helpful.
Maybe make sure your VS Code and Flutter are up to date (I'm using the Master channel/branch which has been updated to dart 2.0 so that might be part of it maybe...).
There are a couple of things you could try. First, it seems as though your push method isn't picking up the right type - I'm fairly sure the error you're seeing is at the .then(... where it expects and Object and you're telling it to be a Listing. You can change your .push to .push<Listing> to force it to return a Listing.
Next, I'm not actually familiar with what you're doing with Function(Listing). I don't think that's proper dart, or at least any more. I think the recommended way of doing that is typedef-ing a method with the right parameter type i.e. typedef void ListingCallback(Listing listing); and then using that as the argument, if you're going to use a callback (see later on in the answer for an arguably better alternative).
The other thing you're doing wrong, which many people seem to do at first in dart/flutter, is mis-using async. If you use async in a method signature, you should not be using .then for futures, but rather using await. See here and this part of effective dart. I've re-written your code to do that properly:
//outside class somewhere
typedef void ListingCallback(Listing listing);
...
_openEditListingDialog(
Listing listing, ListingCallback onSubmittedCallback) async {
Listing newEntry = await Navigator.of(context).push<Listing>(
new MaterialPageRoute<Listing>(
builder: (BuildContext context) {
return new ListingDialog.edit(listing);
},
fullscreenDialog: true,
),
);
if (newEntry != null) {
newEntry.id = listing.id;
onSubmittedCallback(newEntry);
}
}
Note that this also gets rid of that whole function that was causing you problems.
Also, you could just use futures and remove the async in the method signature, but then you're going to have to make sure to actually return a future.
And lastly, you could (should) probably be returning a Future rather than passing in a callback.
Future<Listing> _openEditListingDialog(Listing listing) async {
Listing newEntry = await Navigator.of(context).push<Listing>(
new MaterialPageRoute<Listing>(
builder: (BuildContext context) {
return new ListingDialog.edit(listing);
},
fullscreenDialog: true,
),
);
if (newEntry != null) {
newEntry.id = listing.id;
}
return newEntry;
}
You pass a function
Function(Listing) onSubmittedCallback) async {
// no return here
});
This means Dart infers return type Null
I think changing it to
FutureOr<Null> Function(Listing) onSubmittedCallback) async =>
and removing } at the end (last line of your code) should fix it.
If you want to keep the function body {...} use
FutureOr<Null> Function(Listing) onSubmittedCallback) async {
await Navigator
.of(context)
...

Error when getting ContextMenu and adding new item

So I have controller class with this object:
#FXML
private TextArea textArea;
then Im trying to add new MenuItem to its standard Items(that is "copy" and "select all")
#Override
public void initialize(URL location, ResourceBundle resources) {
ContextMenu contextMenu = textArea.getContextMenu();
X contextMenu.getItems().add(new MenuItem("chuj"));
textArea.setContextMenu(contextMenu);
and line marked with X gets me null pointer exception. Why?
Interesting part is that I can get contextMenu from textArea and set it back to its place with no error. I just cant add something new.
Unfortunately, there's no current way to access the default context menu, which is private API in the TextInputControl. This is a known bug.
If you set a context menu, it will remove the default one. You can recreate most of the functionality in the default context menu as these simply map to public methods defined in TextArea. The exceptions are "undo" and "redo".
So you can do something like this:
private List<MenuItem> createDefaultMenuItems(TextInputControl t) {
MenuItem cut = new MenuItem("Cut");
cut.setOnAction(e -> t.cut());
MenuItem copy = new MenuItem("Copy");
copy.setOnAction(e -> t.copy());
MenuItem paste = new MenuItem("Paste");
paste.setOnAction(e -> t.paste());
MenuItem delete = new MenuItem("Delete");
delete.setOnAction(e -> t.deleteText(t.getSelection()));
MenuItem selectAll = new MenuItem("Select All");
selectAll.setOnAction(e -> t.selectAll());
BooleanBinding emptySelection = Bindings.createBooleanBinding(() ->
t.getSelection().getLength() == 0,
t.selectionProperty());
cut.disableProperty().bind(emptySelection);
copy.disableProperty().bind(emptySelection);
delete.disableProperty().bind(emptySelection);
return Arrays.asList(cut, copy, paste, delete, new SeparatorMenuItem(), selectAll);
}
Now you can do
public void initialize() {
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().addAll(createDefaultMenuItems(textArea));
contextMenu.getItems().add(new MenuItem("chuj"));
textArea.setContextMenu(contextMenu);
}
It's a bit of a hack (replicating functionality, etc) and you lose the undo/redo (which is a real issue); but it's the best I can suggest until they fix the bug. I suggest you vote for it...

java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#

I've read several related posts and even posted and answer here but it seems like I was not able to solve the problem.
I have 3 Activities:
Act1 (main)
Act2
Act3
When going back and forth Act1->Act2 and Act2->Act1 I get no issues
When going Act2->Act3 I get no issues
When going Act3->Act2 I get occasional crashes with the following error: java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#.... This is a ListView cursor.
What I tried:
1. Adding stopManagingCursor(currentCursor);to the onPause() of Act2 so I stop managing the cursor when leaving Act2 to Act3
protected void onPause()
{
Log.i(getClass().getName() + ".onPause", "Hi!");
super.onPause();
saveState();
//Make sure you get rid of the cursor when leaving to another Activity
//Prevents: ...Unable to resume activity... trying to requery an already closed cursor
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
stopManagingCursor(currentCursor);
}
When returning back from Act3 to Act2 I do the following:
private void populateCompetitorsListView()
{
ListAdapter currentListAdapter = getListAdapter();
Cursor currentCursor = null;
Cursor tournamentStocksCursor = null;
if(currentListAdapter != null)
{
currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
if(currentCursor != null)
{
//might be redundant, not sure
stopManagingCursor(currentCursor);
// Get all of the stocks from the database and create the item list
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
((SimpleCursorAdapter)currentListAdapter).changeCursor(tournamentStocksCursor);
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
startManagingCursor(tournamentStocksCursor);
//Create an array to specify the fields we want to display in the list (only name)
String[] from = new String[] {StournamentConstants.TblStocks.COLUMN_NAME, StournamentConstants.TblTournamentsStocks.COLUMN_SCORE};
// and an array of the fields we want to bind those fields to (in this case just name)
int[] to = new int[]{R.id.competitor_name, R.id.competitor_score};
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter tournamentStocks = new SimpleCursorAdapter(this, R.layout.competitor_row, tournamentStocksCursor, from, to);
//tournamentStocks.convertToString(tournamentStocksCursor);
setListAdapter(tournamentStocks);
}
So I make sure I invalidate the cursor and use a different one. I found out that when I go Act3->Act2 the system will sometimes use the same cursor for the List View and sometimes it will have a different one.
This is hard to debug and I was never able to catch a crashing system while debugging. I suspect this has to do with the time it takes to debug (long) and the time it takes to run the app (much shorter, no pause due to breakpoints).
In Act2 I use the following Intent and expect no result:
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Intent intent = new Intent(this, ActivityCompetitorDetails.class);
intent.putExtra(StournamentConstants.App.competitorId, id);
intent.putExtra(StournamentConstants.App.tournamentId, mTournamentRowId);
startActivity(intent);
}
Moving Act1->Act2 Act2->Act1 never gives me trouble. There I use startActivityForResult(intent, ACTIVITY_EDIT); and I am not sure - could this be the source of my trouble?
I would be grateful if anyone could shed some light on this subject. I am interested in learning some more about this subject.
Thanks,D.
I call this a 2 dimensional problem: two things were responsible for this crash:
1. I used startManagingCursor(mItemCursor); where I shouldn't have.
2. I forgot to initCursorAdapter() (for autocomplete) on onResume()
//#SuppressWarnings("deprecation")
private void initCursorAdapter()
{
mItemCursor = mDbHelper.getCompetitorsCursor("");
startManagingCursor(mItemCursor); //<= this is bad!
mCursorAdapter = new CompetitorAdapter(getApplicationContext(), mItemCursor);
initItemFilter();
}
Now it seems to work fine. I hope so...
Put this it may work for you:
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
orderCursor.requery();
}
This also works
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
startManagingCursor(Cursor);
}

Image first loaded, then it isn't? (XNA)

I am very confused at the Moment.
I have the following Class: (Just a part of the class):
public class GUIWindow
{
#region Static Fields
//The standard image for windows.
public static IngameImage StandardBackgroundImage;
#endregion
}
IngameImage is just one of my own classes, but actually it contains a Texture2D (and some other things).
In another class I load a list of GUIButtons by deserializing a XML file.
public static GUI Initializazion(string pXMLPath, ContentManager pConMan)
{
GUI myGUI = pConMan.Load<GUI>(pXMLPath);
GUIWindow.StandardBackgroundImage = new
IngameImage(pConMan.Load<Texture2D>(myGUI.WindowStandardBackgroundImagePath),
Vector2.Zero, 1024, 600, 1, 0, Color.White, 1.0f,
true, false, false);
System.Console.WriteLine("Image loaded? " +
(GUIWindow.StandardBackgroundImage.ImageStrip != null));
myGUI.Windows = pConMan.Load<List<GUIWindow>>(myGUI.GUIFormatXMLPath);
System.Console.WriteLine("Windows loaded");
return myGUI;
}
Here this line: System.Console.WriteLine("Image loaded? " +
(GUIWindow.StandardBackgroundImage.ImageStrip != null));
Prints "true".
To load the GUIWindows I need an "empty" constructor, which looks like that:
public GUIWindow()
{
Name = "";
Buttons = new List<Button>();
ImagePath = "";
System.Console.WriteLine("Image loaded? (In win) " +
(GUIWindow.StandardBackgroundImage.ImageStrip != null));
//Image = new IngameImage(StandardBackgroundImage);
//System.Console.WriteLine(
//Image.IsActive = false;
SelectedButton = null;
IsActive = false;
}
As you can see, I commented lines out in the constructor. Because: Otherwise this would crash.
Here the line System.Console.WriteLine("Image loaded? (In win) " +
(GUIWindow.StandardBackgroundImage.ImageStrip != null));
Doesn't print anything, it just crashes with the following errormessage:
Building content threw NullReferenceException: Object reference not set to an object instance.
Why does this happen?
Before the program wants to load the List, it prints "true". But in the constructor, so in the loading of the list it prints "false".
Can anybody please tell me why this happens and how to fix it?
My best guess at the NullReferenceException is that GUIWindow.StandardBackgroundImage is null, so it throws this exception when you try to access GUIWindow.StandardBackgroundImage.ImageStrip.
Are you familiar with the Visual Studio debugger? If not, you should be. I'd set some breakpoints and step through any code that reads or writes StandardBackgroundImage.
Really, though, your organization could be improved. Why is StandardBackgroundImage a static field of the GUIWindow class? It should be a field of the class which loads it - wherever the Initialization method is. Then pass it into the constructor of GUIWindow.
You are treating the StandardBackgroundImage field like a global, and thus are feeling the effects of that decision - some things are reading and modifying it, and you can't keep track of what order they are doing so.
Take this advice on globals.

Resources