-
Notifications
You must be signed in to change notification settings - Fork 96
Conversation
Thank you, this is a good idea in case one is working on multiple branches. I think a yes/no dialog should be enough though on the reload action. |
What do they do? |
See my updated comment:) By the way, no need to close and reopen the tab, you can just clear the buffer and insert the contents of the new file. One should also try to go the the last cursor position if it's still there, if not somewhere nearby, and do a scroll to that position. |
I went with the path of least resistance here. I think that clearing/replacing the buffer with the contents would require new safety checks by hand. I'll look into it, but I can make no promises for success :) Edit: I can't really figure out how to properly reopen the file another way. I tried by dissecting the Notebook::get().open(path); method and building a proper reload function, but I just don't understand it well enough to do it properly. |
I've experimented quite a bit and I can't get it to work properly. Whenever I try to create a new textiter in the new buffer to place the cursor, the application instead marks the text from offset 0 to the cursor offset and if I manually place the cursor outside of the previously marked text, the whole application crashes with segfault and error code 139 I'm definitely doing something wrong, but I can't figure out what. Same goes if I only replace the current current buffer with the new buffer and try to save (segfault, return code 139) The "this file was changed outside of juCi++" warning also doesn't go away. void Notebook::reload(const boost::filesystem::path &file_path, size_t notebook_index) {
if(boost::filesystem::exists(file_path)) {
std::ifstream can_read(file_path.string());
if(!can_read) {
Terminal::get().print("Error: could not open "+file_path.string()+"\n", true);
return;
}
can_read.close();
}
auto last_view=get_current_view();
Source::View* new_view;
auto language=Source::guess_language(file_path);
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc"))
new_view = new Source::ClangView(file_path, language);
else
new_view = new Source::GenericView(file_path, language);
auto insert = last_view->get_buffer()->get_insert();
int offset = insert->get_iter().get_offset();
auto name = insert->get_name();
auto old_position = new_view->get_iter_at_line_end(0);
old_position.set_offset(offset);
last_view->set_buffer(new_view->get_buffer());
last_view->get_buffer()->create_mark(name, old_position);
last_view->get_buffer()->set_modified(false);
} this is what it currently looks like. It's pretty wrong, obviously, but I don't know how to do it right. The reason for auto old_position = new_view->get_iter_at_line_end(0); Is that I somehow can't create a new TextIter for the new buffer and Gtk really doesn't like it if I don't use a TextIter or mark from the target document (for good reason) |
I don't have time to look at this right now, but you can use this new function I added some weeks back: https://github.com/cppit/jucipp/blob/master/src/source.h#L51. It handles the gtk specifics related to placing the cursor somewhere nearby its previous position after buffer update. |
Ahh, I see now why it's crashing >_> I'm letting an object run out of scope but I'm still referencing a deallocated pointer out of scope... Can't believe I didn't spot that yesterday...
void Notebook::reload(const boost::filesystem::path &file_path, size_t notebook_index) {
if(boost::filesystem::exists(file_path)) {
std::ifstream can_read(file_path.string());
if(!can_read) {
Terminal::get().print("Error: could not open "+file_path.string()+"\n", true);
return;
}
can_read.close();
}
auto last_view=get_current_view();
int offset = last_view->get_buffer()->get_insert()->get_iter().get_offset();
int line = last_view->get_buffer()->get_insert()->get_iter().get_line();
Source::View* new_view;
auto language=Source::guess_language(file_path);
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc"))
new_view = new Source::ClangView(file_path, language);
else
new_view = new Source::GenericView(file_path, language);
auto textbuffer = new_view->get_buffer();
Gtk::TextBuffer *a = textbuffer.release();
Glib::RefPtr<Gtk::TextBuffer> b = Glib::RefPtr<Gtk::TextBuffer>(a);
last_view->set_buffer(b);
last_view->place_cursor_at_line_offset(line, offset);
last_view->get_buffer()->set_modified(true);
}
Edit 2: void Notebook::reload(const boost::filesystem::path &file_path, size_t notebook_index) {
if(boost::filesystem::exists(file_path)) {
std::ifstream can_read(file_path.string());
if(!can_read) {
Terminal::get().print("Error: could not open "+file_path.string()+"\n", true);
return;
}
can_read.close();
}
auto last_view=get_current_view();
int offset = last_view->get_buffer()->get_insert()->get_iter().get_offset();
int line = last_view->get_buffer()->get_insert()->get_iter().get_line();
Source::View* new_view;
auto language=Source::guess_language(file_path);
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc"))
new_view = new Source::ClangView(file_path, language);
else
new_view = new Source::GenericView(file_path, language);
last_view->set_source_buffer(new_view->get_source_buffer());
last_view->place_cursor_at_line_offset(line, offset);
last_view->get_buffer()->set_modified(true);
} This seems to work better, it replaces the buffer nicely, but after around 5 seconds it segfaults again. Edit 3: It's a very very weird bug, because at the moment it doesn't segfault again for some reason, even tho I only made and rolled back a few changes... to this exact state... I'm so confused, pls help. Edit 4: And after a complete recompile it segfaults again... I have no idea what's going on and I have no idea how gdb works... Edit 5: The debugger says it's nullref-ing somewhere. I assume that the view in it's destructor calls something like This is what I'm getting as console output:
Edit 6: If I replace both buffer and source_buffer, it takes a lot longer, but a segfault will still inevitably occur... |
There should be no need to create a new Source::View since the file should have the same extension. Deleting a view is somewhat complex since it has to be done in the background due to there are no way of stopping libclang while it's processing. I think this will solve your issues. |
OK, added a new commit that does what you described and works without any crashes.
Edit: I've chosen to work on the old TextBuffer instead of creating a new one, because that would lose all kinds of important settings, which I'd have to reapply manually. |
Thank you! I did some changes, but it's merged now:) |
Adds a menu option to reload the current file.
Normally when a file is changed from outside of juCi++, it first has to be closed completely and then re-opened in order to work with these changes.
In case of a race while using this PR (a file was changed externally and juCi++ has unsaved changes), upon clicking "Reload File" in the menu, the function will always be executed, but the user is asked if they want to save the current file before it's opened. There's no merging and no backups, either the file on the drive is overwritten or the changes within the editor are overwritten.
To fix this, it would make sense to add an extra warning beforehand, or, instead of an overwriting save, open the "Save File As" prompt, to give the user the option to save his current changes to a separate file and manually merge them afterwards.
I don't know how to do that, so it's not in this pull request.
This pull request addresses this issue I opened earlier: #285
I'm going to try to find out how to solve the race-condition, but I'd like your input on that.