[education/rkward] /: Allow to _not_ be notified about certain symbol changes (those that originated in the frontend, and should not be synced both ways).

Thomas Friedrichsmeier null at kde.org
Thu May 26 10:06:45 BST 2022


Git commit ba67120e52297f32a379fcf67fac25c761feac79 by Thomas Friedrichsmeier.
Committed on 22/05/2022 at 07:41.
Pushed by tfry into branch 'master'.

Allow to _not_ be notified about certain symbol changes (those that originated in the frontend, and should not be synced both ways).

Also merge master. (Accidentally in the same commit, and to difficult to sort out)

M  +3    -0    ChangeLog
M  +1    -1    VERSION.cmake
M  +1    -0    rkward/core/rcontainerobject.cpp
M  +7    -2    rkward/core/rkrownames.cpp
M  +16   -6    rkward/core/rkvariable.cpp
M  +12   -0    rkward/core/robject.cpp
M  +2    -0    rkward/core/robject.h
M  +13   -4    rkward/dataeditor/rkvareditmodel.cpp
M  +7    -0    rkward/rbackend/rcommand.cpp
M  +7    -0    rkward/rbackend/rcommand.h
M  +5    -1    rkward/rbackend/rkrbackend.cpp
M  +1    -0    rkward/rbackend/rkrbackendprotocol_shared.h
M  +26   -19   rkward/rbackend/rkrinterface.h
M  +17   -0    rkward/rbackend/rkrsupport.cpp
M  +2    -0    rkward/rbackend/rkrsupport.h
M  +3    -1    rkward/rbackend/rktransmitter.cpp
M  +2    -2    rkward/rbackend/rpackages/rkward/DESCRIPTION

https://invent.kde.org/education/rkward/commit/ba67120e52297f32a379fcf67fac25c761feac79

diff --cc ChangeLog
index d3228980,d3228980..2b8ccab1
--- a/ChangeLog
+++ b/ChangeLog
@@@ -1,3 -1,3 +1,6 @@@
++- Cache based modification detection
++  - TODO: more testing, check performance impact (while editing data.frame)
++
  --- Version 0.7.4 - May-30-2022
  - Support "step into" and "finish current" modes in the R debugger window
  - When directly upgrading from very old versions (pre 0.6.3, currently), discard existing config settings
diff --cc VERSION.cmake
index 35521b43,8990e133..c5638183
--- a/VERSION.cmake
+++ b/VERSION.cmake
@@@ -1,3 -1,3 +1,3 @@@
  # DO NOT CHANGE THIS FILE MANUALLY!
  # It will be overwritten by scripts/set_dist_version.sh
- SET(RKVERSION_NUMBER 0.7.3z+0.7.4+devel2)
 -SET(RKVERSION_NUMBER 0.7.4z+0.7.5+devel1)
++SET(RKVERSION_NUMBER 0.7.4z+0.7.5+devel2)
diff --cc rkward/core/rcontainerobject.cpp
index 6e5e15d0,6e5e15d0..774142bc
--- a/rkward/core/rcontainerobject.cpp
+++ b/rkward/core/rcontainerobject.cpp
@@@ -316,6 -316,6 +316,7 @@@ void RContainerObject::renameChild (ROb
  	}
  
  	RCommand *command = new RCommand (renameChildCommand (object, new_name), RCommand::App | RCommand::Sync);
++	command->setUpdatesObject(this);
  	RInterface::issueCommand (command, 0);
  
  	object->name = new_name;
diff --cc rkward/core/rkrownames.cpp
index 4b0c95a1,4b0c95a1..23eaaa37
--- a/rkward/core/rkrownames.cpp
+++ b/rkward/core/rkrownames.cpp
@@@ -61,7 -61,7 +61,9 @@@ void RKRowNames::writeData (int from_ro
  	RK_TRACE (OBJECTS);
  
  	if (isSequential ()) {
--		RInterface::issueCommand (getFullName (DefaultObjectNameOptions) + " <- NULL", RCommand::App | RCommand::Sync, QString (), 0,0, chain);
++		RCommand *command = new RCommand(getFullName(DefaultObjectNameOptions) + " <- NULL", RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(this);
++		RInterface::issueCommand(command, chain);
  	} else {
  		// unfortunately, we always need to write the whole data, as row.names<- does not support indexing.
  		QString data_string = "c (";
@@@ -73,7 -73,7 +75,10 @@@
  			}
  		}
  		data_string.append (")");
--		RInterface::issueCommand (getFullName (DefaultObjectNameOptions) + " <- " + data_string, RCommand::App | RCommand::Sync, QString (), 0, 0, chain);
++
++		RCommand* command = new RCommand(getFullName(DefaultObjectNameOptions) + " <- " + data_string, RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(this);
++		RInterface::issueCommand(command, chain);
  	}
  
  	ChangeSet *set = new ChangeSet;
diff --cc rkward/core/rkvariable.cpp
index 055402dd,055402dd..1b85e8ee
--- a/rkward/core/rkvariable.cpp
+++ b/rkward/core/rkvariable.cpp
@@@ -1,6 -1,6 +1,6 @@@
  /*
  rkvariable - This file is part of RKWard (https://rkward.kde.org). Created: Thu Aug 12 2004
--SPDX-FileCopyrightText: 2004-2012 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
++SPDX-FileCopyrightText: 2004-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
  SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
  SPDX-License-Identifier: GPL-2.0-or-later
  */
@@@ -85,7 -85,7 +85,9 @@@ void RKVariable::setVarType (RObject::R
  			else if (new_type == RObject::DataLogical) command += "as.logical";
  			else if (new_type == RObject::DataFactor) command += "as.factor";
  			command += ')';
--			RInterface::issueCommand (command, RCommand::App | RCommand::Sync, QString ());
++			RCommand *rcommand = new RCommand(command, RCommand::App | RCommand::Sync);
++			rcommand->setUpdatesObject(this);
++			RInterface::issueCommand(rcommand);
  			if (new_type == RObject::DataFactor) updateValueLabels ();	// as.factor resets the "levels"-attribute!
  
  			syncDataToR ();
@@@ -364,7 -364,7 +366,9 @@@ void RKVariable::writeInvalidFields (QL
  		if (!values.isEmpty ()) values.append (",");
  	}
  
--	RInterface::issueCommand (".rk.set.invalid.fields (" + getFullName () + ", " + set + values + clear + ')', RCommand::App | RCommand::Sync, QString (), 0,0, chain);
++	RCommand *command = new RCommand(".rk.set.invalid.fields (" + getFullName () + ", " + set + values + clear + ')', RCommand::App | RCommand::Sync);
++	command->setUpdatesObject(this);
++	RInterface::issueCommand(command, chain);
  
  	if (data->previously_valid != data->invalid_fields.isEmpty ()) {
  		data->previously_valid = data->invalid_fields.isEmpty ();
@@@ -380,7 -380,7 +384,9 @@@ void RKVariable::writeData (int from_ro
  
  	// TODO: try to sync in correct storage mode
  	if (from_row == to_row) {
--		RInterface::issueCommand (getFullName () + '[' + QString::number (from_row+1) + "] <- " + getRText (from_row), RCommand::App | RCommand::Sync, QString (), 0,0, chain);
++		RCommand *command = new RCommand(getFullName() + '[' + QString::number(from_row+1) + "] <- " + getRText(from_row), RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(this);
++		RInterface::issueCommand(command, chain);
  		if (data->cell_states[from_row] & RKVarEditData::UnsyncedInvalidState) changed_invalids.append (from_row);
  	} else {
  		QString data_string = "c (";
@@@ -393,7 -393,7 +399,9 @@@
  			if (data->cell_states[row] & RKVarEditData::UnsyncedInvalidState) changed_invalids.append (row);
  		}
  		data_string.append (")");
--		RInterface::issueCommand (getFullName () + '[' + QString::number (from_row + 1) + ':' + QString::number (to_row + 1) + "] <- " + data_string, RCommand::App | RCommand::Sync, QString (), 0,0, chain);
++		RCommand* command = new RCommand(getFullName() + '[' + QString::number(from_row + 1) + ':' + QString::number(to_row + 1) + "] <- " + data_string, RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(this);
++		RInterface::issueCommand(command, chain);
  	}
  
  	if (!changed_invalids.isEmpty ()) writeInvalidFields (changed_invalids, chain);
@@@ -758,7 -758,7 +766,9 @@@ void RKVariable::writeValueLabels (RCom
  		level_string = "NULL";
  	}
  
--	RInterface::issueCommand (".rk.set.levels (" + getFullName () + ", " + level_string + ')', RCommand::App | RCommand::Sync, QString (), 0, 0, chain);
++	RCommand* command = new RCommand(".rk.set.levels(" + getFullName() + ", " + level_string + ')', RCommand::App | RCommand::Sync);
++	command->setUpdatesObject(this);
++	RInterface::issueCommand(command, chain);
  }
  
  QString RKVariable::getValueLabelString () const {
diff --cc rkward/core/robject.cpp
index 028b8d7b,028b8d7b..f7e2d6eb
--- a/rkward/core/robject.cpp
+++ b/rkward/core/robject.cpp
@@@ -202,6 -202,6 +202,7 @@@ void RObject::writeMetaData (RCommandCh
  	}
  
  	RCommand *command = new RCommand (".rk.set.meta (" + getFullName () + ", " + map_string + ')', RCommand::App | RCommand::Sync);
++	command->setUpdatesObject(this);
  	RInterface::issueCommand (command, chain);
  }
  
@@@ -780,6 -780,6 +781,17 @@@ REnvironmentObject* RObject::toplevelEn
  	return static_cast<REnvironmentObject*> (o);
  }
  
++RObject *RObject::globalEnvSymbol() const {
++	RK_TRACE (OBJECTS);
++	RObject *o = const_cast<RObject*>(this);	// it's ok, all we need to do is find the toplevel parent
++	while (o->parent) {
++		if (o->parent == RObjectList::getGlobalEnv()) return o;
++		o = o->parent;
++	}
++	RK_ASSERT(false);
++	return nullptr;
++}
++
  bool RObject::isInGlobalEnv () const {
  	RK_TRACE (OBJECTS);
  
diff --cc rkward/core/robject.h
index 37ba6554,37ba6554..12135a80
--- a/rkward/core/robject.h
+++ b/rkward/core/robject.h
@@@ -152,6 -152,6 +152,8 @@@ public
  	bool isInGlobalEnv () const;
  /** returns the toplevel environment that this object is in. May the same as the object. */
  	REnvironmentObject *toplevelEnvironment () const;
++/** returns the R symbol this object belongs to. E.g. for mylist$sublist$vector returns mylist. May be the same as the object. Behavior should be assumed undefined for object outside of globalenv() */
++	RObject *globalEnvSymbol() const;
  
  	void rename (const QString &new_short_name);
  	void remove (bool removed_in_workspace);
diff --cc rkward/dataeditor/rkvareditmodel.cpp
index ef719692,ef719692..657736bb
--- a/rkward/dataeditor/rkvareditmodel.cpp
+++ b/rkward/dataeditor/rkvareditmodel.cpp
@@@ -847,7 -847,7 +847,9 @@@ bool RKVarEditDataFrameModel::insertCol
  		RK_ASSERT (obj->isVariable ());
  //		addObject (col, obj);	// the object will be added via RKModificationTracker::addObject -> this::childAdded. That will also take care of calling beginInsertColumns()/endInsertColumns()
  	
--		RInterface::issueCommand (new RCommand (".rk.data.frame.insert.column (" + dataframe->getFullName () + ", \"" + obj->getShortName () + "\", " + QString::number (col+1-var_col_offset) + ")", RCommand::App | RCommand::Sync));
++		RCommand* command = new RCommand(".rk.data.frame.insert.column (" + dataframe->getFullName() + ", \"" + obj->getShortName() + "\", " + QString::number(col+1-var_col_offset) + ")", RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(dataframe);
++		RInterface::issueCommand(command);
  	}
  
  	return true;
@@@ -879,7 -879,7 +881,9 @@@ void RKVarEditDataFrameModel::doInsertR
  
  	// TODO: most of the time we're only adding one row at a time, still we should have a function to add multiple rows at once.
  	for (int i = row; i < row + count; ++i) {
--		RInterface::issueCommand (new RCommand (".rk.data.frame.insert.row (" + dataframe->getFullName () + ", " + QString::number (i+1) + ')', RCommand::App | RCommand::Sync));
++		RCommand* command = new RCommand(".rk.data.frame.insert.row(" + dataframe->getFullName() + ", " + QString::number(i+1) + ')', RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(dataframe);
++		RInterface::issueCommand(command);
  	}
  }
  
@@@ -887,7 -887,7 +891,9 @@@ void RKVarEditDataFrameModel::doRemoveR
  	RK_TRACE (EDITOR);
  
  	for (int i = row + count - 1; i >= row; --i) {
--		RInterface::issueCommand (new RCommand (".rk.data.frame.delete.row (" + dataframe->getFullName () + ", " + QString::number (i+1) + ')', RCommand::App | RCommand::Sync));
++		RCommand* command = new RCommand(".rk.data.frame.delete.row(" + dataframe->getFullName() + ", " + QString::number(i+1) + ')', RCommand::App | RCommand::Sync);
++		command->setUpdatesObject(dataframe);
++		RInterface::issueCommand(command);
  	}
  }
  
@@@ -962,7 -962,7 +968,10 @@@ void RKVarEditDataFrameModel::pushTabl
  	command.append (")");
  
  	// push all children
--	RInterface::issueCommand (new RCommand (command, RCommand::Sync), sync_chain);
++	RCommand* rcommand = new RCommand(command, RCommand::Sync);
++	rcommand->setUpdatesObject(dataframe);
++	RInterface::issueCommand(rcommand, sync_chain);
++
  	for (int col=0; col < objects.size (); ++col) {
  		objects[col]->restore (sync_chain);
  	}
diff --cc rkward/rbackend/rcommand.cpp
index 36961fa8,36961fa8..fc275039
--- a/rkward/rbackend/rcommand.cpp
+++ b/rkward/rbackend/rcommand.cpp
@@@ -181,6 -181,6 +181,12 @@@ QString RCommand::fullOutput () const 
  	return ret;
  }
  
++void RCommand::setUpdatesObject(const RObject *object) {
++	RK_TRACE(RBACKEND);
++	RK_ASSERT(_updated_object.isNull());
++	_updated_object = object->globalEnvSymbol()->getShortName();
++}
++
  QString RCommand::remainingCommand () const {
  	RK_TRACE (RBACKEND);
  	RK_ASSERT (_type & User);	// not a grave problem, if it's not, but not useful, either
@@@ -208,6 -208,6 +214,7 @@@ RCommandProxy* RCommand::makeProxy () c
  	RK_ASSERT (getDataType () == RData::NoData);
  
  	RCommandProxy *ret = new RCommandProxy (_command, _type);
++	ret->updates_object = _updated_object;
  	ret->id = _id,
  	ret->status = status;
  	ret->has_been_run_up_to = has_been_run_up_to;
diff --cc rkward/rbackend/rcommand.h
index fa6d29e9,acbea540..617f0ec8
--- a/rkward/rbackend/rcommand.h
+++ b/rkward/rbackend/rcommand.h
@@@ -19,6 -19,6 +19,7 @@@ SPDX-License-Identifier: GPL-2.0-or-lat
  class RCommandReceiver;
  class RCommand;
  class RCommandProxy;
++class RObject;
  
  /** R Commands can be arranged in a simple chain to make sure they are not interrupted by other commands.
   *  Also, command may need to run sub-commands.
@@@ -193,6 -193,6 +194,7 @@@ public
  	ROutputList &getOutput () { return output_list; };
  /** modify the command string. DO NOT CALL THIS after the command has been submitted! */
  	void setCommand (const QString &command) { _command = command; };
++	void setUpdatesObject(const RObject *object);
  
  /** creates a proxy for this RCommand */
  	RCommandProxy* makeProxy () const;
@@@ -219,6 -223,6 +225,7 @@@ friend class RCommandStackModel
  	int status;
  	int has_been_run_up_to;
  	QString _rk_equiv;
++	QString _updated_object;
  	int _id;
  	static int next_id;
  	RCommandReceiver *receivers[MAX_RECEIVERS_PER_RCOMMAND];
diff --cc rkward/rbackend/rkrbackend.cpp
index 33a4f648,1f819bf7..9934a800
--- a/rkward/rbackend/rkrbackend.cpp
+++ b/rkward/rbackend/rkrbackend.cpp
@@@ -1056,13 -1063,9 +1056,13 @@@ SEXP doCaptureOutput (SEXP mode, SEXP c
  
  SEXP RKStartGraphicsDevice (SEXP width, SEXP height, SEXP pointsize, SEXP family, SEXP bg, SEXP title, SEXP antialias);
  SEXP RKD_AdjustSize (SEXP devnum, SEXP id);
 -SEXP doWs (SEXP name);
  void doPendingPriorityCommands ();
  
 +SEXP checkEnv(SEXP a) {
 +	auto res = RKRShadowEnvironment::diffAndUpdate(a);
- 	return RKRSupport::StringListToSEXP(res.added + res.changed + res.removed);
++	return Rf_list3(RKRSupport::StringListToSEXP(res.added), RKRSupport::StringListToSEXP(res.removed), RKRSupport::StringListToSEXP(res.changed));
 +}
 +
  bool RKRBackend::startR () {
  	RK_TRACE (RBACKEND);
  
@@@ -1541,6 -1544,6 +1541,10 @@@ void RKRBackend::commandFinished (bool 
  		current_command->has_been_run_up_to = current_command->command.length () - remainder.length ();
  	}
  
++	if (!current_command->updates_object.isEmpty()) {
++		// Update cached value for objects that are known to have been modified, so as to not trigger an additional change notification.
++		RKRShadowEnvironment::updateCacheForGlobalenvSymbol(current_command->updates_object);
++	}
  	if (check_object_updates_needed || (current_command->type & RCommand::ObjectListUpdate)) {
  		checkObjectUpdatesNeeded (current_command->type & (RCommand::User | RCommand::ObjectListUpdate));
  	}
diff --cc rkward/rbackend/rkrbackendprotocol_shared.h
index 750398e7,750398e7..9b1780d2
--- a/rkward/rbackend/rkrbackendprotocol_shared.h
+++ b/rkward/rbackend/rkrbackendprotocol_shared.h
@@@ -123,6 -123,6 +123,7 @@@ friend class RBackendRequest
  public:		// all these are public for technical reasons, only.
  	~RCommandProxy ();
  	QString command;
++	QString updates_object;
  	int type;
  	int id;
  	int status;
diff --cc rkward/rbackend/rkrsupport.cpp
index 1e2190f1,65942829..d13614c5
--- a/rkward/rbackend/rkrsupport.cpp
+++ b/rkward/rbackend/rkrsupport.cpp
@@@ -269,82 -269,3 +269,99 @@@ RData *RKRSupport::SEXPToRData (SEXP fr
  
  	return data;
  }
 +
 +SEXP RKRShadowEnvironment::shadowenvbase = nullptr;
 +QMap<SEXP, RKRShadowEnvironment*> RKRShadowEnvironment::environments;
 +RKRShadowEnvironment* RKRShadowEnvironment::environmentFor(SEXP baseenvir) {
++	RK_TRACE(RBACKEND);
 +	// TODO: probably R_GlobalEnv should be special-cased, as this is what we'll check most often (or exclusively?)
 +	if (!environments.contains(baseenvir)) {
 +		RK_DEBUG(RBACKEND, DL_DEBUG, "creating new shadow environment for %p\n", baseenvir);
 +		if (!shadowenvbase) {
 +			SEXP rkn = Rf_allocVector(STRSXP, 1);
 +			SET_STRING_ELT(rkn, 0, Rf_mkChar("package:rkward"));
 +			SEXP rkwardenv = RKRSupport::callSimpleFun(Rf_install("as.environment"), rkn, R_GlobalEnv);
 +			RK_ASSERT(Rf_isEnvironment(rkwardenv));
 +			SEXP rkwardvars = Rf_eval(Rf_findVar(Rf_install(".rk.variables"), rkwardenv), R_BaseEnv);  // NOTE: Rf_eval to resolve promise
 +			RK_ASSERT(Rf_isEnvironment(rkwardvars));
 +			shadowenvbase = Rf_findVar(Rf_install(".rk.shadow.envs"), rkwardvars);
 +			RK_ASSERT(Rf_isEnvironment(shadowenvbase));
 +		}
 +
 +		char name[sizeof(void*)*2+3];
 +		sprintf(name, "%p", baseenvir);
 +		SEXP tr = Rf_allocVector(LGLSXP, 1);
 +		LOGICAL(tr)[0] = true;
 +		Rf_defineVar(Rf_install(name), RKRSupport::callSimpleFun2(Rf_install("new.env"), tr, R_EmptyEnv, R_GlobalEnv), shadowenvbase);
 +		SEXP shadowenvir = Rf_findVar(Rf_install(name), shadowenvbase);
 +		environments.insert(baseenvir, new RKRShadowEnvironment(baseenvir, shadowenvir));
 +	}
 +	return environments[baseenvir];
 +}
 +
 +static bool nameInList(SEXP needle, SEXP haystack) {
 +	int count = Rf_length(haystack);
 +	for (int i = 0; i < count; ++i) {
 +		if (!strcmp(R_CHAR(needle), R_CHAR(STRING_ELT(haystack, i)))) return true;
 +	}
 +	return false;
 +}
 +
++void RKRShadowEnvironment::updateCacheForGlobalenvSymbol(const QString& name) {
++	RK_DEBUG(RBACKEND, DL_DEBUG, "updating cached value for symbol %s", qPrintable(name));
++	environmentFor(R_GlobalEnv)->updateSymbolCache(name);
++}
++
++void RKRShadowEnvironment::updateSymbolCache(const QString& name) {
++	RK_TRACE(RBACKEND);
++	SEXP rname = Rf_installChar(Rf_mkCharCE(name.toUtf8(), CE_UTF8));
++	PROTECT(rname);
++	SEXP symbol_g = Rf_findVar(rname, R_GlobalEnv);
++	PROTECT(symbol_g);
++	Rf_defineVar(rname, symbol_g, shadowenvir);
++	UNPROTECT(2);
++}
++
 +RKRShadowEnvironment::Result RKRShadowEnvironment::diffAndUpdate() {
++	RK_TRACE (RBACKEND);
 +	Result res;
 +
 +	SEXP symbols = R_lsInternal(baseenvir, TRUE);
 +	PROTECT(symbols);
 +	int count = Rf_length(symbols);
 +	SEXP symbols2 = R_lsInternal(shadowenvir, TRUE);
 +	PROTECT(symbols2);
 +	int count2 = Rf_length (symbols2);
 +
 +	// find the changed symbols, and copy them to the shadow environment
 +	for (int i = 0; i < count; ++i) {
 +		SEXP name = Rf_installChar(STRING_ELT(symbols, i));
 +		PROTECT(name);
 +		SEXP main = Rf_findVar(name, baseenvir);
 +		SEXP cached = Rf_findVar(name, shadowenvir);
 +		if (main != cached) {
 +			Rf_defineVar(name, main, shadowenvir);
 +			if (/*Rf_isNull(cached) && */ !Rf_isNull(main) && !nameInList(STRING_ELT(symbols, i), symbols2)) {
 +				res.added.append(RKRSupport::SEXPToString(name));
 +			} else {
 +				res.changed.append(RKRSupport::SEXPToString(name));
 +			}
 +		}
 +		UNPROTECT(1);
 +	}
 +
 +	// find the symbols only in the shadow environment (those that were removed)
 +	for (int i = 0; i < count2; ++i) {
 +		if (!nameInList(STRING_ELT(symbols2, i), symbols)) {
 +			res.removed.append(RKRSupport::SEXPToString(Rf_installChar(STRING_ELT(symbols2, i))));
 +			R_removeVarFromFrame(Rf_installChar(STRING_ELT(symbols2, i)), shadowenvir);
 +		}
 +	}
 +
 +	UNPROTECT(2); // symbols, symbols2
 +
 +	RK_DEBUG(RBACKEND, DL_DEBUG, "added %s\n", qPrintable(res.added.join(", ")));
 +	RK_DEBUG(RBACKEND, DL_DEBUG, "changed %s\n", qPrintable(res.changed.join(", ")));
 +	RK_DEBUG(RBACKEND, DL_DEBUG, "removed %s\n", qPrintable(res.removed.join(", ")));
 +	return res;
 +}
diff --cc rkward/rbackend/rkrsupport.h
index 177d296d,976d387a..84090727
--- a/rkward/rbackend/rkrsupport.h
+++ b/rkward/rbackend/rkrsupport.h
@@@ -35,24 -35,4 +35,26 @@@ namespace RKRSupport 
  	RData* SEXPToRData (SEXP from_exp);
  };
  
 +class RKRShadowEnvironment {
 +public:
 +	struct Result {
 +		QStringList added;
 +		QStringList removed;
 +		QStringList changed;
 +		bool isEmpty() const { return added.isEmpty() && removed.isEmpty() && changed.isEmpty(); };
 +	};
 +	Result diffAndUpdate();
 +	static Result diffAndUpdate(SEXP envir) { return environmentFor(envir)->diffAndUpdate(); };
++	static void updateCacheForGlobalenvSymbol(const QString &name);
 +private:
 +	RKRShadowEnvironment(SEXP baseenvir, SEXP shadowenvir) : baseenvir(baseenvir), shadowenvir(shadowenvir) {};
 +	~RKRShadowEnvironment();
 +	static RKRShadowEnvironment* environmentFor(SEXP baseenvir);
++	void updateSymbolCache(const QString &name);
 +	SEXP baseenvir;
 +	SEXP shadowenvir;
 +	static QMap<SEXP, RKRShadowEnvironment*> environments;
 +	static SEXP shadowenvbase;
 +};
 +
  #endif
diff --cc rkward/rbackend/rktransmitter.cpp
index 2bed158c,2bed158c..0d9c2960
--- a/rkward/rbackend/rktransmitter.cpp
+++ b/rkward/rbackend/rktransmitter.cpp
@@@ -1,6 -1,6 +1,6 @@@
  /*
  rktransmitter - This file is part of RKWard (https://rkward.kde.org). Created: Thu Nov 18 2010
--SPDX-FileCopyrightText: 2010-2013 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
++SPDX-FileCopyrightText: 2010-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
  SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
  SPDX-License-Identifier: GPL-2.0-or-later
  */
@@@ -159,6 -159,6 +159,7 @@@ void RKRBackendSerializer::serializePro
  	stream << (qint32) proxy.id;
  	stream << (qint32) proxy.status;
  	stream << (qint32) proxy.has_been_run_up_to;
++	stream << proxy.updates_object;
  
  	serializeData (proxy, stream);
  }
@@@ -179,6 -179,6 +180,7 @@@ RCommandProxy* RKRBackendSerializer::un
  	ret->status = dummy32;
  	stream >> dummy32;
  	ret->has_been_run_up_to = dummy32;
++	stream >> (ret->updates_object);
  
  	RData *data = unserializeData (stream);
  	ret->swallowData (*data);
diff --cc rkward/rbackend/rpackages/rkward/DESCRIPTION
index 8f498d00,0525e70e..5cdb6c31
--- a/rkward/rbackend/rpackages/rkward/DESCRIPTION
+++ b/rkward/rbackend/rpackages/rkward/DESCRIPTION
@@@ -17,8 -17,8 +17,8 @@@ Authors at R: c(person(given="Thomas", fam
          email="thomas.friedrichsmeier at ruhr-uni-bochum.de",
          role=c("aut")), person(given="the RKWard team",
          email="rkward-devel at kde.org", role=c("cre","ctb")))
- Version: 0.7.4
- Date: 2022-05-19
+ Version: 0.7.5
 -Date: 2022-05-20
++Date: 2022-05-22
  RoxygenNote: 7.1.2
  Collate: 
      'base_overrides.R'



More information about the rkward-tracker mailing list