I am working on a plugin for IntelliJ Idea 13. I do some changes in beforeDocumentSaving and I use document.setText:
public class AppendAction implements ApplicationComponent
{
#Override public void initComponent()
{
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter()
{
#Override public void beforeDocumentSaving(Document document)
{
document.setText(appendSomething(document.getText()));
}
});
}
}
This works great, my only problem is that when this plugin is run, and I want to undo the changes, I get to following error message:
Cannot Undo
Following files have changes that cannot be undone:
Any Idea? :-)
The answer is wrapping the document.setText into ApplicationManager.getApplication().runWriteAction and CommandProcessor.getInstance().runUndoTransparentAction.
I found an example TrailingSpacesStripper among intellij-community sources on githib:
https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java
public class AppendAction implements ApplicationComponent
{
#Override public void initComponent()
{
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter()
{
#Override public void beforeDocumentSaving(final Document document)
{
ApplicationManager.getApplication().runWriteAction(new DocumentRunnable(document, null)
{
#Override public void run()
{
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable()
{
#Override public void run()
{
document.setText(appendSomething(document.getText()));
}
});
}
});
}
});
}
}
You should wrap the change through the CommandProcessor API.
From IntelliJ IDEA Architectural Overview:
Any operations which modify the contents of the document must be wrapped in a command (CommandProcessor.getInstance().executeCommand()). executeCommand() calls can be nested, and the outermost executeCommand call is added to the undo stack. If multiple documents are modified within a command, undoing this command will by default show a confirmation dialog to the user.
Related
I am using the Sumup SDK to create a bridge to React Native. Most of the hard work is done but I am trying to call a specific function to wake up the card reader before a transaction is processed.
The original code I had was this:
#ReactMethod
public void prepareCardTerminal() {
SumUpAPI.prepareForCheckout();
}
}
The RN bridge then calls this function like this:
static prepareCardTerminal() {
NativeRNSumup.prepareCardTerminal();
}
How ever this gives me a React Native error of:
Must be called on main thread
I read that this could mean it needs to be run on the UI thread so I rewrote the function to be:
#ReactMethod
public void prepareCardTerminal() {
new Thread(new Runnable() {
public void run() {
SumUpAPI.prepareForCheckout();
}
});
}
However this doesn't have the intended results (even though it doesn't show any errors).
Any tips would be much appreciated.
Edit: I found a solution to this issue. I used UiThreadUtil:
import com.facebook.react.bridge.UiThreadUtil;
...
#ReactMethod
public void prepareCardTerminal() {
UiThreadUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
});
}
You can do something like this:
#ReactMethod
public void prepareCardTerminal() {
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
};
mainHandler.post(myRunnable);
}
Or even simpler:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
});
The answer posted as an edit to the question is correct. The answer by #waquar-ulhaq is technically correct but using UiThreadUtil is way simpler and internally does use a Handler
import com.facebook.react.bridge.UiThreadUtil;
...
#ReactMethod
public void prepareCardTerminal() {
UiThreadUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
SumUpAPI.prepareForCheckout();
}
});
}
I'm fairly new to RxJava and I have a basic understanding as to how to wrap a callback into an Observable but what I'm having difficulty with is doing so when the callback/listener is pre-instanced. Every example that I have found only shows instancing the callback directly into the Observable being created.
Some example code of what I'm talking about. I'm working with an Api that's works like this:
public class Api {
private ApiCallback callback;
void initialize(ApiCallback callback){
this.callback = callback;
}
void doAction1(){
this.callback.onAction1Complete();
}
}
interface ApiCallback {
void onInitialized();
void onAction1Complete();
}
With the real api I am working with I have no control over how it works so I must work with it in this state. In terms of trying to work with this Api using observables here is the struggle I am having. I have a member variable that holds the Api object:
private Api mApi = new Api();
Now in order to initialize this I have one of two options it seems.
Option 1:
Completable startApi() {
return Completable.create(new CompletableOnSubscribe() {
#Override
public void subscribe(final CompletableEmitter emitter) throws Exception {
mApi.initialize(new ApiCallback() {
#Override
public void onInitialized() {
emitter.onComplete();
}
#Override
public void onAction1Complete() {
}
});
}
});
}
Option 2:
private ApiCallback premadeCallback = new ApiCallback() {
#Override
public void onInitialized() {
}
#Override
public void onAction1Complete() {
}
};
Completable startApi() {
return Completable.create(new CompletableOnSubscribe() {
#Override
public void subscribe(final CompletableEmitter emitter) throws Exception {
mApi.initialize(premadeCallback);
}
});
}
Now the issue I have is that Option 2 makes more sense to me when I need to know when the other methods in the callback are called from Api calls. With my understanding of RxJava however I don't understand how I can reach these method calls with an Api that works like this.
For example:
Completable doAction1() {
return Completable.create(new CompletableOnSubscribe() {
#Override
public void subscribe(final CompletableEmitter emitter) throws Exception {
// Api is already initialized with callback
// How do I reach the callback from here?
}
});
}
The only what that I can currently think of as to how to achieve this would be to create a member variable as an emitter (or a dictionary of emitters) and then call its appropriate method in the api callback when needed. My concerns with this are A. I'm unsure if RxJava can work this way B. This sounds like a terrible idea.
I have this function that downloads all files in the ArrayList and I want to make this "synchronous" , I mean that I want to download only one file at time.
How can I make the FOR cycle to wait until a file is downloaded and than take an other file to download?
public void downloadFiles(ArrayList<String> files, final String destinationFolder){
for(String file:files){
GoogleDrive.getInstance().readFile(file, GoogleDrive.FolderLocation.ROOT_FOLDER, new GoogleDrive.GoogleDriveReadFileCallback() {
#Override
public void successful(String title, byte[] content) {
try {
FileUtils.writeByteArrayToFile(new File(destinationFolder+File.pathSeparator+title), content);
} catch (IOException e) {
Log.e(TAG,"ERROR FileManager.downloadFiles: "+e.toString());
}
}
#Override
public void notFound() { }
#Override
public void error(String error) { }
});
}
}
The question sounds pretty simple; but turns out to be hard. Why is that? Because the given code is doing things in a wrong way. What do I mean with that?
I assume that
GoogleDrive.getInstance().readFile(file,
GoogleDrive.FolderLocation.ROOT_FOLDER,
new GoogleDrive.GoogleDriveReadFileCallback()
triggers an asynchronous read from Google Drive; and upon competition, that callback instance will be called. But when we have a closer look into that callback code - we find that it is missing essential parts:
it is not doing any kind of error handling (hint: you have no idea when something went wrong with this approach)
the callback has no means to "signal" to the outside world "i am done".
Thus: the solution is to rework that thing completely. You could create a real class implementing the required interface; and that callback implementation could have methods that tell you whether file reading is still ongoing, completed successfully or failed.
In other words: you build a wrapper around GoogleDrive readFile(); and that wrapper offers synchronous reading (probably successfull() gets called when the readFile() is done - so your wrapper can simply wait for that callback); or the wrapper could return some sort of Future.
24 hours later the answear was too easy, just implemented a listener that start a new download every time an old one is terminated(with success or not) and remove it from the list. I don't know if this is the correct way to do it but it works
interface FileManagerDownloadEvent{
void downloadSuccessful(String fileName);
void downloadNotFound(String fileName);
void downloadError(String fileName,String error);
}
public class FileManager implements FileManagerDownloadEvent{
private FileManagerDownloadEvent downloadEvent;
private ArrayList<String> filesToDownload;
private String destinationFolder;
public FileManager(){
this.downloadEvent=this;
}
private void download(){
if(filesToDownload.size()!=0) {
final String file=filesToDownload.get(0);
filesToDownload.remove(0);
GoogleDrive.getInstance().readFile(file, GoogleDrive.FolderLocation.ROOT_FOLDER, new GoogleDrive.GoogleDriveReadFileCallback() {
#Override
public void successful(String title, byte[] content) {
try {
FileUtils.writeByteArrayToFile(new File(destinationFolder+File.separator+title), content);
downloadEvent.downloadSuccessful(destinationFolder+File.separator+title);
} catch (Exception e) {
Log.e(TAG,"ERROR FileManager.downloadFiles: "+e.toString());
}
}
#Override
public void notFound() {
downloadEvent.downloadNotFound(file);
}
#Override
public void error(String error) {
downloadEvent.downloadError(file,error);
}
});
}
}
#Override
public void downloadSuccessful(String filePath) {
Log.d(TAG,"downloadSuccessful: "+filePath);
download();
}
#Override
public void downloadNotFound(String fileName) {
Log.e(TAG,"downloadNotFound: "+fileName);
download();
}
#Override
public void downloadError(String fileName,String error) {
Log.e(TAG,"downloadError: "+fileName+" --> "+error);
download();
}
}
I have a JFace application and want to do some work on startup. So I overrode the open method of the window.
But now I have the problem that in the case of a failure I can't display an error message because the shell is null at this time. And I have no idea to solve this problem.
public class MyExample extends ApplicationWindow {
public MyExample() {
super(null);
}
public void run() {
setBlockOnOpen(true);
open();
Display.getCurrent().dispose();
}
#Override
public int open() {
// do some work
if (...) {
MessageDialog.openError(getShell(), "Error", "Error occured");
}
return super.open();
}
}
I would try:
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", "Message");
}
});
EDIT:
The static method Display.getDefault() returns the default Display thread or a new one is created if it did not already exist.
On the other hand, the static method Display.getCurrent() returns the Display instance from the currently running thread, or null if the currently running thread is not a user-interface thread for any display.
See more on the Java Documentation of the Display class.
You may also want to take a look at the difference between syncExec() and asyncExec().
I have a view that I want to react to what happens in the editor. Right now I have a button that I want it so that when clicked it updates the data in the view to some new set of information. Where do I start, I have my selection event but no idea on how to communicate between the two. I'm looking for a loose coupling solution.
I'm sure there are many ways of doing this, but I've used the JFace IPropertyChangeListener interface in the past for simple event propagation.
Make your view implement IPropertyChangeListener. Create a Singleton class that you can register your IPropertyChangeListener with, and send a PropertyChangeEvent to. Then in the constructor of your view, register it with your Singleton.
Now you can get hold of your Singleton in your editor and fire off an event that will get picked up in your view.
Example code for the Singleton:
public class PropertyChangeEventBus {
private static PropertyChangeEventBus s_instance = new PropertyChangeEventBus();
public static PropertyChangeEventBus instance()
{
return s_instance;
}
private Set<IPropertyChangeListener> m_listeners;
private PropertyChangeEventBus()
{
// use CopyOnWriteArraySet to prevent ConcurrentModificationExceptions
m_listeners = new CopyOnWriteArraySet<IPropertyChangeListener>();
}
public void addListener(IPropertyChangeListener listener)
{
m_listeners.add(listener);
}
public void removeListener(IPropertyChangeListener listener)
{
m_listeners.remove(listener);
}
public void fire(final PropertyChangeEvent event)
{
// run property change events in UI thread to prevent having to have lots of syncExecs in the listener methods
ViewUtils.syncExec(new Runnable()
{
#Override
public void run()
{
for (IPropertyChangeListener listener : m_listeners)
{
try
{
listener.propertyChange(event);
}
catch(Exception e)
{
//log it, present error message
}
}
}
});
}
}
Example Code for the View:
//The constructor
public MyView()
{
PropertyChangeEventBus.instance().addListener(this);
}
#Override
public void propertyChange(PropertyChangeEvent event)
{
if(event.getProperty().equals(SOME_CONSTANT))
{
// Refresh View
}
}