[rkward-cvs] SF.net SVN: rkward:[3207] trunk/rkward/rkward
tfry at users.sourceforge.net
tfry at users.sourceforge.net
Tue Nov 23 11:17:44 UTC 2010
Revision: 3207
http://rkward.svn.sourceforge.net/rkward/?rev=3207&view=rev
Author: tfry
Date: 2010-11-23 11:17:44 +0000 (Tue, 23 Nov 2010)
Log Message:
-----------
Proper interleaving of commands lines and output for mutli-line commands entered in the R Console.
Also simplifies the code for pasting / piping in the R Console a bit, and provide more useful options when console is busy while trying to pipe a command.
Fix initialization of .libPaths().
Modified Paths:
--------------
trunk/rkward/rkward/rbackend/rcommand.cpp
trunk/rkward/rkward/rbackend/rcommand.h
trunk/rkward/rkward/rbackend/rcommandreceiver.cpp
trunk/rkward/rkward/rbackend/rcommandreceiver.h
trunk/rkward/rkward/rbackend/rcommandstack.h
trunk/rkward/rkward/rbackend/rinterface.cpp
trunk/rkward/rkward/rbackend/rkfrontendtransmitter.cpp
trunk/rkward/rkward/rbackend/rkrbackend.cpp
trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h
trunk/rkward/rkward/rbackend/rktransmitter.cpp
trunk/rkward/rkward/rkconsole.cpp
trunk/rkward/rkward/rkconsole.h
trunk/rkward/rkward/syntax/rkward.xml
trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp
Modified: trunk/rkward/rkward/rbackend/rcommand.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rcommand.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rcommand.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -55,6 +55,7 @@
else _command = command;
if (_command.isEmpty ()) _type |= EmptyCommand;
status = 0;
+ has_been_run_up_to = 0;
_rk_equiv = rk_equiv;
for (int i = 0; i < MAX_RECEIVERS_PER_RCOMMAND; ++i) receivers[i] = 0;
if (!(type & Internal)) {
@@ -122,9 +123,19 @@
}
}
-QString RCommand::error () {
+void RCommand::commandLineIn () {
RK_TRACE (RBACKEND);
+ RK_ASSERT (_type & User);
+ for (int i=0; i < MAX_RECEIVERS_PER_RCOMMAND; ++i) {
+ if (receivers[i] == 0) continue;
+ receivers[i]->userCommandLineIn (this);
+ }
+}
+
+QString RCommand::error () const {
+ RK_TRACE (RBACKEND);
+
QString ret;
for (ROutputList::const_iterator it = output_list.begin (); it != output_list.end (); ++it) {
if ((*it)->type == ROutput::Error) {
@@ -134,7 +145,7 @@
return ret;
}
-QString RCommand::output () {
+QString RCommand::output () const {
RK_TRACE (RBACKEND);
QString ret;
@@ -146,7 +157,7 @@
return ret;
}
-QString RCommand::warnings () {
+QString RCommand::warnings () const {
RK_TRACE (RBACKEND);
QString ret;
@@ -158,7 +169,7 @@
return ret;
}
-QString RCommand::fullOutput () {
+QString RCommand::fullOutput () const {
RK_TRACE (RBACKEND);
QString ret;
@@ -168,6 +179,13 @@
return ret;
}
+QString RCommand::remainingCommand () const {
+ RK_TRACE (RBACKEND);
+ RK_ASSERT (_type & User); // not a grave problem, if it's not, but not useful, either
+
+ return _command.mid (has_been_run_up_to);
+}
+
void RCommand::mergeAndDeleteProxy (RCommandProxy *proxy) {
RK_TRACE (RBACKEND);
@@ -176,6 +194,7 @@
RK_ASSERT (proxy->type == _type);
status = proxy->status;
+ has_been_run_up_to = proxy->has_been_run_up_to;
swallowData (*proxy);
delete proxy;
}
@@ -183,10 +202,12 @@
RCommandProxy* RCommand::makeProxy () const {
RK_TRACE (RBACKEND);
RK_ASSERT (status == 0); // Initialization from an already touched command is not a real problem, but certainly no expected usage
+ RK_ASSERT (has_been_run_up_to == 0);
RK_ASSERT (getDataType () == RData::NoData);
RCommandProxy *ret = new RCommandProxy (_command, _type);
ret->id = _id,
ret->status = status;
+ ret->has_been_run_up_to = has_been_run_up_to;
return ret;
}
Modified: trunk/rkward/rkward/rbackend/rcommand.h
===================================================================
--- trunk/rkward/rkward/rbackend/rcommand.h 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rcommand.h 2010-11-23 11:17:44 UTC (rev 3207)
@@ -53,6 +53,8 @@
@see RInterface::startChain
@see RInterface::closeChain */
class RCommandChain : public RCommandBase {
+public:
+ bool isClosed () const { return closed; };
protected:
friend class RCommandStack;
friend class RCommandStackModel;
@@ -108,25 +110,27 @@
/** destructor. Note: you should not delete RCommands manually. This is done in RInterface. TODO: make protected */
~RCommand();
/** @returns the type as specified in RCommand::RCommand */
- int type () { return _type; };
+ int type () const { return _type; };
/** @returns the raw command status. @see CommandStatus */
- int getStatus () { return status; };
+ int getStatus () const { return status; };
/** @returns the rk_equiv as specified in RCommand::RCommand */
- QString rkEquivalent () { return _rk_equiv; };
+ QString rkEquivalent () const { return _rk_equiv; };
/** @returns the command string (i.e. the input) as specified in RCommand::RCommand */
- QString command () { return _command; };
+ QString command () const { return _command; };
+/** @returns like command(), but for user commands, which have been run, partially, returns only the remaining portion of the command. */
+ QString remainingCommand () const;
/** Each RCommand is assigned a unique integer id (incrementing from 0 to integer overflow) upon creation. This returns this id.
@returns the unique id of this command */
- int id () { return _id; };
+ int id () const { return _id; };
/* TODO: Adjust these two functions to allow re-getting of output and error-messages from logs */
/** @returns the full output of the command, i.e. all "regular" output, warning messages, and errors, in the order they were encountered. @see RCommand::output @see RCommand::error @see RCommand::warnings */
- QString fullOutput ();
+ QString fullOutput () const;
/** @returns the "regular" (ROutput::Output) output of the command, if any (e.g. "[1] 1" for "print (1)"). @see RCommand::succeeded @see RCommand::hasOutput */
- QString output ();
+ QString output () const;
/** @returns the warning message(s) given by R, if any. @see RCommand::output @see RCommand::error */
- QString warnings ();
+ QString warnings () const;
/** @returns the error message given by R, if any. @see RCommand::failed @see RCommand::hasError */
- QString error ();
+ QString error () const;
/** Types of commands (potentially more to come), bitwise or-able,
although partially exclusive. See \ref UsingTheInterfaceToR for a overview of what these are used for. TODO: find out, why Canceled is in here, and document that fact. */
enum CommandTypes {
@@ -159,25 +163,25 @@
Canceled=8192 /**< Command was cancelled. */
};
/** the command has been passed to the backend. */
- bool wasTried () { return (status & WasTried); };
+ bool wasTried () const { return (status & WasTried); };
/** the command failed */
- bool failed () { return (status & Failed); };
+ bool failed () const { return (status & Failed); };
/** the command was cancelled before it was executed */
- bool wasCanceled () { return (wasTried () && failed () && (status & Canceled)); }
+ bool wasCanceled () const { return (wasTried () && failed () && (status & Canceled)); }
/** the command succeeded (wasTried () && (!failed ()) */
- bool succeeded () { return ((status & WasTried) && !(status & Failed)); };
+ bool succeeded () const { return ((status & WasTried) && !(status & Failed)); };
/** command has a string output retrievable via RCommand::output () */
- bool hasOutput () { return (status & HasOutput); };
+ bool hasOutput () const { return (status & HasOutput); };
/** command has a string output retrievable via RCommand::warnings () */
- bool hasWarnings () { return (status & HasWarnings); };
+ bool hasWarnings () const { return (status & HasWarnings); };
/** command has an error-message retrievable via RCommand::error () */
- bool hasError () { return (status & HasError); };
+ bool hasError () const { return (status & HasError); };
/** backend rejected command as being incomplete */
- bool errorIncomplete () { return (status & ErrorIncomplete); };
+ bool errorIncomplete () const { return (status & ErrorIncomplete); };
/** backend rejected command as having a syntax error */
- bool errorSyntax () { return (status & ErrorSyntax); };
+ bool errorSyntax () const { return (status & ErrorSyntax); };
/** return the flags associated with the command. Those are the same that you specified in the constructor, RKWard does not touch them. @see RCommand::RCommand */
- int getFlags () { return (_flags); };
+ int getFlags () const { return (_flags); };
/** Add an additional listener to the command */
void addReceiver (RCommandReceiver *receiver);
/** Remove a receiver from the list. This may be needed when a listener wants to self-destruct, to make sure we don't try to send any further info there */
@@ -198,11 +202,14 @@
void finished ();
/** new output was generated. Pass on to receiver(s) */
void newOutput (ROutput *output);
+/** next line of command has been transmitted. Pass on to receiver(s). Only called for RCommand::User type commands */
+ void commandLineIn ();
ROutputList output_list;
QString _command;
int _type;
int _flags;
- int status;
+ int status;
+ int has_been_run_up_to;
QString _rk_equiv;
int _id;
static int next_id;
Modified: trunk/rkward/rkward/rbackend/rcommandreceiver.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rcommandreceiver.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rcommandreceiver.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -2,7 +2,7 @@
rcommandreceiver - description
-------------------
begin : Thu Aug 19 2004
- copyright : (C) 2004, 2006, 2007 by Thomas Friedrichsmeier
+ copyright : (C) 2004, 2006, 2007, 2010 by Thomas Friedrichsmeier
email : tfry at users.sourceforge.net
***************************************************************************/
@@ -35,14 +35,6 @@
}
}
-void RCommandReceiver::rCommandDone (RCommand *) {
- RK_TRACE (RBACKEND);
-}
-
-void RCommandReceiver::newOutput (RCommand *, ROutput *) {
- RK_TRACE (RBACKEND);
-}
-
void RCommandReceiver::cancelOutstandingCommands () {
RK_TRACE (RBACKEND);
Modified: trunk/rkward/rkward/rbackend/rcommandreceiver.h
===================================================================
--- trunk/rkward/rkward/rbackend/rcommandreceiver.h 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rcommandreceiver.h 2010-11-23 11:17:44 UTC (rev 3207)
@@ -2,7 +2,7 @@
rcommandreceiver - description
-------------------
begin : Thu Aug 19 2004
- copyright : (C) 2004, 2006, 2007 by Thomas Friedrichsmeier
+ copyright : (C) 2004, 2006, 2007, 2010 by Thomas Friedrichsmeier
email : tfry at users.sourceforge.net
***************************************************************************/
@@ -48,11 +48,14 @@
friend class RInterface;
/** This function is called when a command for this receiver is finished (and before it is deleted). You have to implement it in your subclass to do the actual handling.
@param command A pointer to the command. The pointer is still valid during this call, but the RCommand will be deleted shortly after! */
- virtual void rCommandDone (RCommand *);
+ virtual void rCommandDone (RCommand *) {};
/** This function is called when there is new output for a command or this receiver. Default implementation does nothing. Reimplement if you want to get at a command's output immediately (i.e. before the command is fully completed).
@param command A pointer to the command
@param output The new output-fragment */
- virtual void newOutput (RCommand *, ROutput *);
+ virtual void newOutput (RCommand *, ROutput *) {};
+/** This function is called when a new line of the given command has been fed to the backend. Note: This is only ever called for commands of type RCommand::User.
+ at param command A pointer to the command */
+ virtual void userCommandLineIn (RCommand*) {};
protected:
RCommandList outstanding_commands;
void cancelOutstandingCommands ();
Modified: trunk/rkward/rkward/rbackend/rcommandstack.h
===================================================================
--- trunk/rkward/rkward/rbackend/rcommandstack.h 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rcommandstack.h 2010-11-23 11:17:44 UTC (rev 3207)
@@ -50,6 +50,7 @@
/** see pop() */
RCommand* currentCommand ();
+ RCommandChain* currentChain () { return current_chain; };
/** the regular command stack, i.e. not a callback */
static RCommandStack *regular_stack;
Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -264,6 +264,21 @@
for (unsigned int i = 0; i < command->getDataLength (); ++i) {
RKSettingsModuleRPackages::defaultliblocs.append (command->getStringVector ()[i]);
}
+
+ RCommandStack *stack = RCommandStack::currentStack ();
+ RCommandChain *chain = stack->currentChain ();
+ RK_ASSERT (chain);
+ RK_ASSERT (!chain->isClosed ());
+
+ // apply user configurable run time options
+ QStringList commands = RKSettingsModuleR::makeRRunTimeOptionCommands () + RKSettingsModuleRPackages::makeRRunTimeOptionCommands () + RKSettingsModuleOutput::makeRRunTimeOptionCommands () + RKSettingsModuleGraphics::makeRRunTimeOptionCommands ();
+ for (QStringList::const_iterator it = commands.begin (); it != commands.end (); ++it) {
+ issueCommand (*it, RCommand::App | RCommand::Sync, QString (), this, SET_RUNTIME_OPTS, chain);
+ }
+ // initialize output file
+ issueCommand ("rk.set.output.html.file (\"" + RKSettingsModuleGeneral::filesPath () + "/rk_out.html\")\n", RCommand::App | RCommand::Sync, QString (), this, SET_RUNTIME_OPTS, chain);
+
+ closeChain (chain);
} else if (command->getFlags () == GET_HELP_BASE) {
RK_ASSERT (command->getDataType () == RData::StringVector);
RK_ASSERT (command->getDataLength () == 1);
@@ -319,15 +334,7 @@
// start help server / determined help base url
issueCommand (".rk.getHelpBaseUrl ()\n", RCommand::GetStringVector | RCommand::App | RCommand::Sync, QString (), this, GET_HELP_BASE, chain);
- // apply user configurable run time options
- QStringList commands = RKSettingsModuleR::makeRRunTimeOptionCommands () + RKSettingsModuleRPackages::makeRRunTimeOptionCommands () + RKSettingsModuleOutput::makeRRunTimeOptionCommands () + RKSettingsModuleGraphics::makeRRunTimeOptionCommands ();
- for (QStringList::const_iterator it = commands.begin (); it != commands.end (); ++it) {
- issueCommand (*it, RCommand::App | RCommand::Sync, QString (), this, SET_RUNTIME_OPTS, chain);
- }
- // initialize output file
- issueCommand ("rk.set.output.html.file (\"" + RKSettingsModuleGeneral::filesPath () + "/rk_out.html\")\n", RCommand::App | RCommand::Sync, QString (), this, SET_RUNTIME_OPTS, chain);
-
- closeChain (chain);
+ // NOTE: more initialization commands get run *after* we have determined the standard library locations (see rCommandDone())
} else {
processRBackendRequest (request);
}
@@ -620,7 +627,15 @@
// first, copy out the type. Allows for easier typing below
RBackendRequest::RCallbackType type = request->type;
- if (type == RBackendRequest::ShowMessage) {
+ if (type == RBackendRequest::CommandLineIn) {
+ int id = request->params["commandid"].toInt ();
+ RCommand *command = all_current_commands.value (0, 0); // User command will always be the first.
+ if ((command == 0) || (command->id () != id)) {
+ RK_ASSERT (false);
+ } else {
+ command->commandLineIn ();
+ }
+ } else if (type == RBackendRequest::ShowMessage) {
QString caption = request->params["caption"].toString ();
QString message = request->params["message"].toString ();
QString button_yes = request->params["button_yes"].toString ();;
Modified: trunk/rkward/rkward/rbackend/rkfrontendtransmitter.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkfrontendtransmitter.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rkfrontendtransmitter.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -128,6 +128,7 @@
writeRequest (request); // to tell the backend, that we are keeping up. Also deletes the request.
return;
}
+
RKRBackendEvent* event = new RKRBackendEvent (request);
qApp->postEvent (RKRBackendProtocolFrontend::instance (), event);
}
Modified: trunk/rkward/rkward/rbackend/rkrbackend.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackend.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rkrbackend.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -161,10 +161,13 @@
bool reached_eof = false;
int pos = 0;
+ bool reached_newline = false;
while (pos < (buflen-1)) {
buf[pos] = *current_buffer;
- if (*current_buffer == '\n') break;
- else if (*current_buffer == ';') break;
+ if (*current_buffer == '\n') {
+ reached_newline = true;
+ break;
+ } else if (*current_buffer == ';') break;
else if (*current_buffer == '\0') {
reached_eof = true;
break;
@@ -178,6 +181,13 @@
RKRBackend::repl_status.user_command_completely_transmitted = true;
}
buf[++pos] = '\0';
+
+ if (reached_newline) {
+ // Making this request synchronous is a bit painful. However, without this, it's extremely difficult to get correct interleaving of output and command lines
+ RBackendRequest req (true, RBackendRequest::CommandLineIn);
+ req.params["commandid"] = RKRBackend::this_pointer->current_command->id;
+ RKRBackend::this_pointer->handleRequest (&req);
+ }
}
int RReadConsole (const char* prompt, unsigned char* buf, int buflen, int hist) {
@@ -1082,6 +1092,15 @@
current_command->status -= (current_command->status & RCommand::Running);
current_command->status |= RCommand::WasTried;
+ if (current_command->type & RCommand::User) {
+ RK_ASSERT (repl_status.eval_depth == 0);
+
+ // This method may look a bit over-complex, but remember that repl_status.user_command_successful_up_to works on an *encoded* buffer
+ QByteArray remainder_encoded = repl_status.user_command_buffer.mid (repl_status.user_command_successful_up_to);
+ QString remainder = current_locale_codec->toUnicode (remainder_encoded);
+ current_command->has_been_run_up_to = current_command->command.length () - remainder.length ();
+ }
+
if (check_object_updates_needed || (current_command->type & RCommand::ObjectListUpdate)) {
checkObjectUpdatesNeeded (current_command->type & (RCommand::User | RCommand::ObjectListUpdate));
}
Modified: trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h 2010-11-23 11:17:44 UTC (rev 3207)
@@ -43,6 +43,7 @@
CallbackRequest,
HistoricalSubstackRequest,
SetParamsFromBackend,
+ CommandLineIn, /**< The next line of the current user command has been submitted in the backend. */
#ifndef RKWARD_THREADED
Output, /**< A piece of output. Note: If the backend runs in a single process, output is handled in a pull fashion, instead of using requests. */
Interrupt, /**< Interrupt evaluation. This request type originates in the frontend, not the backend (the only one so far). */
@@ -123,6 +124,7 @@
int type;
int id;
int status;
+ int has_been_run_up_to;
};
class RKROutputBuffer {
Modified: trunk/rkward/rkward/rbackend/rktransmitter.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rktransmitter.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rbackend/rktransmitter.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -165,6 +165,7 @@
stream << (qint32) proxy.type;
stream << (qint32) proxy.id;
stream << (qint32) proxy.status;
+ stream << (qint32) proxy.has_been_run_up_to;
serializeData (proxy, stream);
}
@@ -182,6 +183,8 @@
ret->id = dummy32;
stream >> dummy32;
ret->status = dummy32;
+ stream >> dummy32;
+ ret->has_been_run_up_to = dummy32;
RData *data = unserializeData (stream);
ret->swallowData (*data);
Modified: trunk/rkward/rkward/rkconsole.cpp
===================================================================
--- trunk/rkward/rkward/rkconsole.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rkconsole.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -119,7 +119,6 @@
nprefix = "> ";
iprefix = "+ ";
prefix = nprefix;
- command_incomplete = false;
output_continuation = false;
// KDE4: a way to do this?
// doc->setUndoSteps (0);
@@ -130,8 +129,9 @@
commands_history_position = commands_history.constEnd ();
current_command = 0;
+ current_command_displayed_up_to = 0;
tab_key_pressed_before = false;
- command_was_piped = false;
+ previous_chunk_was_piped = false;
// KDE4 TODO: workaround for KDE 4 pre-release versions. Hope this gets fixed before 4.0
// see http://lists.kde.org/?l=kwrite-devel&m=119721420603507&w=2
@@ -199,10 +199,8 @@
}
bool RKConsole::handleKeyPress (QKeyEvent *e) {
-
KTextEditor::Cursor c = view->cursorPosition ();
int para=c.line (); int pos=c.column ();
- command_was_piped = false;
if (para < doc->lines () - 1 || pos < prefix.length ()) { // not inside the last line?
int key = e->key ();
@@ -231,6 +229,8 @@
return true;
}
+ previous_chunk_was_piped = false;
+
if (e->key () == Qt::Key_Up) {
commandsListUp (RKSettingsModuleConsole::shouldDoHistoryContextSensitive (e->modifiers ()));
return true;
@@ -279,6 +279,7 @@
return true;
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
hinter->hideArgHint ();
+ addCommandToHistory (currentEditingLine ());
submitCommand ();
return true;
} else if (e->key () == Qt::Key_Left){
@@ -335,7 +336,7 @@
if (line_rev == 0) {
*cursor_position = currentCursorPositionInCommand ();
- *context = currentCommand ();
+ *context = currentEditingLine ();
} else {
*cursor_position = -1;
*context = incomplete_command;
@@ -416,7 +417,7 @@
void RKConsole::doTabCompletion () {
RK_TRACE (APP);
- QString current_line = currentCommand ();
+ QString current_line = currentEditingLine ();
int current_line_num = doc->lines () - 1;
int word_start;
int word_end;
@@ -514,12 +515,14 @@
return false;
}
-QString RKConsole::currentCommand () {
+QString RKConsole::currentEditingLine () const {
RK_TRACE (APP);
+
+ if (current_command) return QString (); // it doesn't count as "editing" line, if a command is currently being executed
return (doc->line (doc->lines () - 1).mid (prefix.length ()));
}
-void RKConsole::setCurrentCommand (const QString &command) {
+void RKConsole::setCurrentEditingLine (const QString &command) {
RK_TRACE (APP);
int lastline = doc->lines () - 1;
@@ -543,21 +546,34 @@
void RKConsole::submitCommand () {
RK_TRACE (APP);
- QString current_line = currentCommand ();
- QString command = current_line;
- addCommandToHistory (current_line);
-
- if (command_incomplete) {
- command.prepend (incomplete_command + '\n');
+ QString command = incomplete_command + currentEditingLine ();
+ if (!input_buffer.isEmpty ()) {
+ int last_line_end = input_buffer.lastIndexOf ('\n');
+ if (last_line_end < 0) {
+ last_line_end = 0;
+ RK_ASSERT (false);
+ }
+ command.append (input_buffer.left (last_line_end));
+ if (last_line_end < (input_buffer.size () - 1)) {
+ input_buffer = input_buffer.mid (last_line_end + 1);
+ } else {
+ input_buffer.clear ();
+ }
}
+ current_command_displayed_up_to = incomplete_command.length ();
+ setCurrentEditingLine (command.mid (current_command_displayed_up_to, command.indexOf ('\n', current_command_displayed_up_to) - current_command_displayed_up_to));
+ current_command_displayed_up_to += currentEditingLine ().length ();
- doc->insertLine (doc->lines (), QString ());
- if (!current_line.isEmpty ()) {
- current_command = new RCommand (command, RCommand::User | RCommand::Console, QString::null, this);
+ skip_command_display_lines = incomplete_command.count ('\n'); // incomplete command has already been shown.
+
+ if (!command.isEmpty ()) {
+ doc->insertLine (doc->lines (), QString ());
+ current_command = new RCommand (command, RCommand::User | RCommand::Console, QString (), this);
RKGlobals::rInterface ()->issueCommand (current_command);
interrupt_command_action->setEnabled (true);
} else {
- tryNextInBatch ();
+ showPrompt (true);
+ tryNextInBuffer ();
}
}
@@ -565,11 +581,11 @@
RK_TRACE (APP);
// if we are at the last line, i.e. not yet navigating the command history, store the current command
- if (commands_history.constEnd () == commands_history_position) history_editing_line = currentCommand ();
+ if (commands_history.constEnd () == commands_history_position) history_editing_line = currentEditingLine ();
if (context_sensitive) {
if (command_edited) {
- command_history_context = currentCommand ();
+ command_history_context = currentEditingLine ();
commands_history_position = commands_history.constEnd ();
command_edited = false;
}
@@ -589,7 +605,7 @@
if (found) { // if we did not find a previous matching line, do not touch the commands_history_position
commands_history_position = it;
- setCurrentCommand (*commands_history_position);
+ setCurrentEditingLine (*commands_history_position);
} else {
KApplication::kApplication ()->beep ();
}
@@ -600,7 +616,7 @@
if (context_sensitive) {
if (command_edited) {
- command_history_context = currentCommand ();
+ command_history_context = currentEditingLine ();
commands_history_position = commands_history.constEnd ();
command_edited = false;
return; // back at bottommost item
@@ -620,30 +636,32 @@
}
}
- if (commands_history.constEnd () == commands_history_position) setCurrentCommand (history_editing_line);
- else setCurrentCommand (*commands_history_position);
+ if (commands_history.constEnd () == commands_history_position) setCurrentEditingLine (history_editing_line);
+ else setCurrentEditingLine (*commands_history_position);
}
void RKConsole::rCommandDone (RCommand *command) {
RK_TRACE (APP);
+
+ current_command = 0;
+
if (command->errorSyntax () && command->error ().isEmpty ()) {
doc->insertLine (doc->lines () - 1, i18n ("Syntax error\n"));
}
if (command->errorIncomplete ()) {
prefix = iprefix;
- command_incomplete = true;
- incomplete_command = command->command ();
+ incomplete_command = command->remainingCommand ();
} else {
prefix = nprefix;
- command_incomplete = false;
- incomplete_command = QString::null;
+ incomplete_command.clear ();
}
if (output_continuation) doc->insertLine (doc->lines (), "");
output_continuation = false;
commands_history_position = commands_history.constEnd ();
- tryNextInBatch ();
+ showPrompt (true);
+ tryNextInBuffer ();
}
void RKConsole::newOutput (RCommand *, ROutput *output) {
@@ -666,7 +684,7 @@
if (RKSettingsModuleConsole::maxConsoleLines ()) {
uint c = (uint) doc->lines();
-// We remove the superflous lines in chunks of 20 while handling output for better performance. Later, in tryNextInBatch(), we trim down to the correct size.
+// We remove the superflous lines in chunks of 20 while handling output for better performance. Later, in showPrompt(), we trim down to the correct size.
if (c > (RKSettingsModuleConsole::maxConsoleLines () + 20)) {
// KDE4: does the setUpdatesEnabled (false) still affect performance?
view->setUpdatesEnabled (false); // major performance boost while removing lines!
@@ -678,19 +696,34 @@
output_continuation = true;
}
+void RKConsole::userCommandLineIn (RCommand* cmd) {
+ RK_TRACE (APP);
+ RK_ASSERT (cmd == current_command);
+
+ if (--skip_command_display_lines >= 0) return;
+
+ QString line = cmd->command ().mid (current_command_displayed_up_to + 1);
+ line = line.section ('\n', 0, 0) + '\n';
+ current_command_displayed_up_to += line.length ();
+ if (line.length () < 2) return; // omit empty lines (esp. the trailing newline of the command!)
+
+ prefix = iprefix;
+ showPrompt (!doc->line (doc->lines ()-1).isEmpty ());
+ setCurrentEditingLine (line);
+}
+
void RKConsole::submitBatch (const QString &batch) {
RK_TRACE (APP);
- if (current_command) return;
- // splitting batch, not allowing empty entries.
- // TODO: hack something so we can have empty entries.
- commands_batch = batch.split ("\n", QString::SkipEmptyParts);
- tryNextInBatch (false);
+ previous_chunk_was_piped = false;
+ input_buffer.append (batch);
+ if (!current_command) tryNextInBuffer ();
}
-void RKConsole::tryNextInBatch (bool add_new_line) {
+void RKConsole::showPrompt (bool add_new_line) {
RK_TRACE (APP);
- if (add_new_line) {
+
+ if (add_new_line || (!doc->lines ())) {
if (RKSettingsModuleConsole::maxConsoleLines ()) {
uint c = doc->lines();
if (c > RKSettingsModuleConsole::maxConsoleLines ()) {
@@ -700,27 +733,34 @@
view->setUpdatesEnabled (true);
}
}
- if (!doc->lines ()) doc->insertLine (0, QString ());
- doc->insertText (KTextEditor::Cursor (doc->lines () - 1, 0), prefix);
-// doc->insertLine (doc->lines (), prefix); // somehow, it seems to be safer to do this after removing superfluous lines, than before
- cursorAtTheEnd ();
+ doc->insertLine (doc->lines (), QString ());
}
+ doc->insertText (KTextEditor::Cursor (doc->lines () - 1, 0), prefix);
+// doc->insertLine (doc->lines (), prefix); // somehow, it seems to be safer to do this after removing superfluous lines, than before
+ cursorAtTheEnd ();
+}
- if (!commands_batch.isEmpty()) {
- // If we were not finished executing a batch of commands, we execute the next one.
- setCurrentCommand (currentCommand () + commands_batch.first ());
- commands_batch.pop_front ();
- if (!commands_batch.isEmpty ()){
- submitCommand ();
+void RKConsole::tryNextInBuffer () {
+ RK_TRACE (APP);
+
+ if (!input_buffer.isEmpty ()) {
+ if (input_buffer.contains ('\n')) {
+ submitCommand (); // will submit and clear the buffer
return;
+ } else {
+ setCurrentEditingLine (currentEditingLine () + input_buffer);
+ input_buffer.clear ();
}
- // We would put this here if we would want the last line to be executed. We generally don't want this, as there is an empty last item, if there is a newline at the end.
- //TODO: deal with this kind of situation better.
- //commands_batch.erase(commands_batch.begin());
}
+ interrupt_command_action->setEnabled (false);
+}
- current_command = 0;
- interrupt_command_action->setEnabled (isBusy ());
+bool RKConsole::isBusy () const {
+ if (current_command) return true;
+ if (!incomplete_command.isEmpty ()) return true;
+ if (!currentEditingLine ().isEmpty ()) return true;
+ if (!input_buffer.isEmpty ()) return true;
+ return false;
}
void RKConsole::paste () {
@@ -732,7 +772,7 @@
void RKConsole::clear () {
RK_TRACE (APP);
doc->clear ();
- tryNextInBatch ();
+ showPrompt ();
}
void RKConsole::addCommandToHistory (const QString &command) {
@@ -775,51 +815,40 @@
QApplication::clipboard()->setText (view->selectionText ());
}
-int RKConsole::currentCursorPosition (){
+int RKConsole::currentCursorPosition () const {
KTextEditor::Cursor c = view->cursorPosition ();
return(c.column ());
}
int RKConsole::currentCursorPositionInCommand(){
RK_TRACE (APP);
- return(currentCursorPosition() - prefix.length());
+ return (currentCursorPosition() - prefix.length());
}
-void RKConsole::resetIncompleteCommand () {
+void RKConsole::resetConsole () {
RK_TRACE (APP);
-
- RK_ASSERT (command_incomplete);
- prefix = nprefix;
- command_incomplete = false;
- incomplete_command = QString::null;
- doc->insertLine (doc->lines (), "");
-
- tryNextInBatch (true);
-}
-
-void RKConsole::slotInterruptCommand () {
- RK_TRACE (APP);
- RK_ASSERT (current_command || command_incomplete);
RK_DO (qDebug("received interrupt signal in console"), APP, DL_DEBUG);
- commands_batch.clear ();
- if (command_incomplete) {
- resetIncompleteCommand ();
+ input_buffer.clear ();
+ if (current_command) {
+ RKGlobals::rInterface ()->cancelCommand (current_command);
} else {
- RKGlobals::rInterface ()->cancelCommand (current_command);
+ prefix = nprefix;
+ incomplete_command.clear ();
+
+ showPrompt (true);
}
}
void RKConsole::runSelection () {
RK_TRACE (APP);
- QString command = cleanedSelection ();
- pipeUserCommand (new RCommand (command, RCommand::User));
+ pipeUserCommand (cleanedSelection ());
}
void RKConsole::showContextHelp () {
RK_TRACE (APP);
- RKHelpSearchWindow::mainHelpSearch ()->getContextHelp (currentCommand (), currentCursorPositionInCommand ());
+ RKHelpSearchWindow::mainHelpSearch ()->getContextHelp (currentEditingLine (), currentCursorPositionInCommand ());
}
void RKConsole::initializeActions (KActionCollection *ac) {
@@ -829,7 +858,7 @@
run_selection_action = RKStandardActions::runSelection (this, this, SLOT (runSelection()));
- interrupt_command_action = ac->addAction ("interrupt", this, SLOT (slotInterruptCommand()));
+ interrupt_command_action = ac->addAction ("interrupt", this, SLOT (resetConsole()));
interrupt_command_action->setText (i18n ("Interrupt running command"));
interrupt_command_action->setShortcut (Qt::ControlModifier + Qt::Key_C);
interrupt_command_action->setIcon (KIcon ("media-playback-stop"));
@@ -856,55 +885,36 @@
void RKConsole::pipeUserCommand (const QString &command) {
RK_TRACE (APP);
- RCommand *cmd = new RCommand (command, RCommand::User);
- pipeUserCommand (cmd);
-}
-
-void RKConsole::pipeUserCommand (RCommand *command) {
- RK_TRACE (APP);
-
if (RKSettingsModuleConsole::pipeUserCommandsThroughConsole ()) {
RKConsole::mainConsole ()->pipeCommandThroughConsoleLocal (command);
} else {
- RKGlobals::rInterface ()->issueCommand (command);
+ RCommand *cmd = new RCommand (command, RCommand::User);
+ RKGlobals::rInterface ()->issueCommand (cmd);
}
}
-void RKConsole::pipeCommandThroughConsoleLocal (RCommand *command) {
+void RKConsole::pipeCommandThroughConsoleLocal (const QString &command_string) {
RK_TRACE (APP);
activate (false);
- if ((!command_was_piped) && (isBusy () || (!currentCommand ().isEmpty ()))) {
- int res = KMessageBox::questionYesNo (this, i18n ("You have configured RKWard to run script commands through the console. However, the console is currently busy (either a command is running, or you have started to enter text in the console). Do you want to bypass the console this one time, or do you want to try again later?"), i18n ("Console is busy"), KGuiItem (i18n ("Bypass console")), KGuiItem (i18n ("Cancel")));
- if (res == KMessageBox::Yes) {
- RKGlobals::rInterface ()->issueCommand (command);
- } else {
- delete command;
+ if (isBusy () && (!previous_chunk_was_piped)) {
+ int res = KMessageBox::questionYesNoCancel (this, i18n ("You have configured RKWard to pipe script editor commands through the R Console. However, another command is currently active in the console. Do you want to append it to the command in the console, or do you want to reset the console, first? Press cancel if you do not wish to run the new command, now."), i18n ("R Console is busy"), KGuiItem (i18n ("Append")), KGuiItem (i18n ("Reset, then submit")));
+ if (res == KMessageBox::No) {
+ resetConsole ();
+ } else if (res != KMessageBox::Yes) {
+ return;
}
- } else {
- QString command_string = command->command ();
- QString text = command_string;
- if (RKSettingsModuleConsole::addPipedCommandsToHistory() != RKSettingsModuleConsole::DontAdd) {
- QStringList lines = text.split ('\n', QString::SkipEmptyParts);
- if ((RKSettingsModuleConsole::addPipedCommandsToHistory() == RKSettingsModuleConsole::AlwaysAdd) || (lines.count () == 1)) {
- for (int i = 0; i < lines.count (); ++i) {
- addCommandToHistory (lines[i]);
- }
+ }
+ if (RKSettingsModuleConsole::addPipedCommandsToHistory() != RKSettingsModuleConsole::DontAdd) {
+ QStringList lines = command_string.split ('\n', QString::SkipEmptyParts);
+ if ((RKSettingsModuleConsole::addPipedCommandsToHistory() == RKSettingsModuleConsole::AlwaysAdd) || (lines.count () == 1)) {
+ for (int i = 0; i < lines.count (); ++i) {
+ addCommandToHistory (lines[i]);
}
}
- text.replace ('\n', QString ("\n") + iprefix);
- doc->insertText (KTextEditor::Cursor (doc->lines () - 1, QString (nprefix).length ()), text + '\n');
- command->addReceiver (this);
- command->addTypeFlag (RCommand::Console);
- current_command = command;
- if (command_incomplete) {
- RK_ASSERT (command_was_piped);
- command_string.prepend (incomplete_command + '\n');
- command->setCommand (command_string);
- }
- command_was_piped = true;
- RKGlobals::rInterface ()->issueCommand (command);
}
+ submitBatch (command_string + '\n');
+ previous_chunk_was_piped = true;
}
void RKConsole::contextMenuEvent (QContextMenuEvent * event) {
Modified: trunk/rkward/rkward/rkconsole.h
===================================================================
--- trunk/rkward/rkward/rkconsole.h 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/rkconsole.h 2010-11-23 11:17:44 UTC (rev 3207)
@@ -56,22 +56,18 @@
~RKConsole ();
/** Returns the command currently being edited (not executed yet) */
- QString currentCommand ();
+ QString currentEditingLine () const;
/** Returns the current cursor position. Returns the column on which is the cursor. */
- int currentCursorPosition ();
+ int currentCursorPosition () const;
/** Returns the current cursor position, within the current command (without taking into account the prefix).*/
int currentCursorPositionInCommand ();
-/** interrupt the current incomplete command (if any) */
- void resetIncompleteCommand ();
void doTabCompletion ();
bool provideContext (unsigned int line_rev, QString *context, int *cursor_position);
static RKConsole *mainConsole () { return main_console; };
static void setMainConsole (RKConsole *console) { main_console = console; };
- bool isBusy () { return (current_command || command_incomplete); };
-/** Run a user command (through console, if applicable */
- static void pipeUserCommand (RCommand *command);
+ bool isBusy () const;
/** Overload for the above function: Use this, if you just need to run a string with no specials */
static void pipeUserCommand (const QString &command);
@@ -104,8 +100,6 @@
QString history_editing_line;
/** The context to look out for, if doing a context search in the command history */
QString command_history_context;
-/** A list to store a commands batch that will be executed one line at a time */
- QStringList commands_batch;
/** Sets the cursor position to the end of the last line. */
void cursorAtTheEnd ();
/** Submits the current command */
@@ -120,9 +114,10 @@
void cursorAtTheBeginning ();
/** Sets the current command. This is used from commandsListUp (), and commandsListDown ();
\param command the new command */
- void setCurrentCommand (const QString &command);
-/** Add a new line, and try to submit the next item in a batch of (pasted) commands. If there is no batch, only add the new line. */
- void tryNextInBatch (bool add_new_line = true);
+ void setCurrentEditingLine (const QString &line);
+/** Try to submit the next chunk of the input buffer. */
+ void tryNextInBuffer ();
+ void showPrompt (bool add_new_line = false);
/** Add given command to command history. Also checks, wether the history is longer than max length, and chops it if so. */
void addCommandToHistory (const QString &command);
@@ -163,8 +158,7 @@
void setCursorClear (int line, int col);
void initializeActions (KActionCollection *ac);
- void pipeCommandThroughConsoleLocal (RCommand *command);
- bool command_was_piped;
+ void pipeCommandThroughConsoleLocal (const QString &command);
RKConsolePart *console_part;
public slots:
@@ -177,13 +171,30 @@
void clear ();
/** show context help on the current word */
void showContextHelp ();
-/** interrupt current command. */
- void slotInterruptCommand ();
+/** Cancels the current command, and clears the command buffer */
+ void resetConsole ();
void runSelection ();
/** Submits a batch of commands, line by line.
\param batch a QString containing the batch of commands to be executed */
void submitBatch (const QString &batch);
+private:
+/** Commands can be queued in the console in four different places:
+1) The not-yet-executed remainder of a previous incomplete command.
+2) A command which has already been issued. Note that this command, too, be incomplete, in which case it's remainder will be stored to 1.
+3) One or more lines of commands which have been pasted or piped to the Console, but have not yet been submitted.
+4) One line of a command without a trailing newline. This may be part of a previously pasted command, but most typically it is the line the user is currently editing.
+
+1 and 2 are mutually exclusive, and stored in incomplete_command, or current_command->command ().
+3 is stored in input_buffer. If there is no current command, the input buffer will be emptied as soon as tryNextInBatch() is called.
+4 and 2 are mutually exclusive. 4 can be retrieved as currentEditingLine () */
+ QString input_buffer;
+ int current_command_displayed_up_to;
+ int skip_command_display_lines;
+ bool previous_chunk_was_piped;
+
+/** Reimplemented from RCommandReceiver to display the next line of the command */
+ void userCommandLineIn (RCommand* command);
};
/** A part interface to RKConsole. Provides the context-help functionality
Modified: trunk/rkward/rkward/syntax/rkward.xml
===================================================================
--- trunk/rkward/rkward/syntax/rkward.xml 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/syntax/rkward.xml 2010-11-23 11:17:44 UTC (rev 3207)
@@ -9,7 +9,7 @@
R : http://www.r-project.org/
RKWard : http://rkward.sourceforge.net/
-->
-<language version="2.02" kateversion="2.5" name="RKWard output" section="Scripts" extensions="" mimetype="" author="P. Ecochard" license="GPL">
+<language version="2.03" kateversion="2.5" name="R interactive session" section="Scripts" extensions="" mimetype="" author="P. Ecochard" license="GPL">
<highlighting>
<list name="controls">
<item> for </item>
@@ -46,6 +46,7 @@
<!-- The output context does no highlighting. If a "> " is encountered at line start, pop back to ctxbase (and to command0 from there) -->
<context attribute="Output" lineEndContext="#stay" name="output">
<Detect2Chars attribute="Prompt" context="#pop" char=">" char1=" " column="0" lookAhead="true" endRegion="commandRegion"/>
+ <Detect2Chars attribute="Prompt" context="#pop" char="+" char1=" " column="0" lookAhead="true"/>
</context>
<!-- The base level context in command mode. Does not allow closing '}' -->
@@ -98,10 +99,10 @@
<!-- This context is not really used, but meant to be included. It checks whether a new line is a continuation of a command. If so, it eats the "+ " at the line start. Else it tries to pop back to the top level -->
<context attribute="Normal Text" name="CommandContinuationCheck" lineEndContext="#stay">
<!-- Since this rule is included in every (relevant) context, this will unwind the entire context stack back to ctxbase. -->
- <RegExpr attribute="Prompt" context="#pop" String="^> " lookAhead="true" endRegion="commandRegion"/>
+ <Detect2Chars attribute="Prompt" context="#pop" char=">" char1=" " column="0" lookAhead="true" endRegion="commandRegion"/>
<!-- See above -->
- <RegExpr attribute="Normal Text" context="#pop" String="^([^\+]|$)" lookAhead="true"/>
- <RegExpr attribute="Prompt" context="#stay" String="^\+ "/>
+ <RegExpr attribute="Normal Text" context="output" String="^([^\+]|$)" lookAhead="true"/>
+ <Detect2Chars attribute="Prompt" context="#stay" char=">" char1=" " column="0" lookAhead="true"/>
</context>
<!-- This context is not really used, but contains the common rules -->
Modified: trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp 2010-11-22 10:15:14 UTC (rev 3206)
+++ trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp 2010-11-23 11:17:44 UTC (rev 3207)
@@ -588,7 +588,7 @@
// KURL::directory () returns a leading slash on windows as of KDElibs 4.3
while (dir.startsWith ('/')) dir.remove (0, 1);
#endif
- RKConsole::pipeUserCommand (new RCommand ("setwd (\"" + dir + "\")", RCommand::User, i18n ("cd to current script directory")));
+ RKConsole::pipeUserCommand ("setwd (\"" + dir + "\")");
}
void RKCommandEditorWindow::runSelection() {
@@ -597,7 +597,7 @@
QString command = m_view->selectionText ();
if (command.isEmpty ()) return;
- RKConsole::pipeUserCommand (new RCommand (command, RCommand::User, QString::null));
+ RKConsole::pipeUserCommand (command);
}
void RKCommandEditorWindow::runLine() {
@@ -605,7 +605,7 @@
KTextEditor::Cursor c = m_view->cursorPosition();
QString command = m_doc->line (c.line());
- if (!command.isEmpty ()) RKConsole::pipeUserCommand (new RCommand (command, RCommand::User, QString::null));
+ if (!command.isEmpty ()) RKConsole::pipeUserCommand (command);
// advance to next line (NOTE: m_view->down () won't work on auto-wrapped lines)
c.setLine(c.line() + 1);
@@ -638,7 +638,7 @@
QString command = m_doc->text (*(block_records[index].range));
if (command.isEmpty ()) return;
- RKConsole::pipeUserCommand (new RCommand (command, RCommand::User, QString::null));
+ RKConsole::pipeUserCommand (command);
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the rkward-tracker
mailing list