Command history separated from Console.

develop
Petr Mrázek 2011-08-13 14:42:09 +02:00
parent 1cbcb99dd5
commit 81e6bce92c
10 changed files with 178 additions and 116 deletions

@ -140,6 +140,23 @@ namespace DFHack
{
//sync();
}
private:
bool read_char(unsigned char & out)
{
while(1)
{
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0)
return false;
if (FD_ISSET(STDIN_FILENO, &descriptor_set))
{
// read byte from stdin
read(STDIN_FILENO, &out, 1);
return true;
}
if (FD_ISSET(exit_pipe[0], &descriptor_set))
return false;
}
}
protected:
int sync()
{
@ -267,7 +284,7 @@ namespace DFHack
/// beep. maybe?
//void beep (void);
/// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output, mutex * lock)
int lineedit(const std::string& prompt, std::string& output, mutex * lock, CommandHistory & ch)
{
output.clear();
this->prompt = prompt;
@ -288,7 +305,7 @@ namespace DFHack
if(state == con_lineedit)
return -1;
state = con_lineedit;
count = prompt_loop(lock);
count = prompt_loop(lock,ch);
state = con_unclaimed;
disable_raw();
print("\n");
@ -299,19 +316,6 @@ namespace DFHack
return count;
}
}
/// add a command to the history
void history_add(const std::string& command)
{
// if current command = last in history -> do not add. Always add if history is empty.
if(!history.empty() && history.front() == command)
return;
history.push_front(command);
if(history.size() > 100)
history.pop_back();
}
/// clear the command history
void history_clear();
int enable_raw()
{
struct termios raw;
@ -381,7 +385,7 @@ namespace DFHack
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
}
int prompt_loop(mutex * lock)
int prompt_loop(mutex * lock, CommandHistory & history)
{
int fd = STDIN_FILENO;
size_t plen = prompt.size();
@ -391,18 +395,20 @@ namespace DFHack
/* The latest history entry is always our current buffer, that
* initially is just an empty string. */
const std::string empty;
history_add(empty);
history.add(empty);
if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1;
while(1)
{
char c;
int nread;
char seq[2], seq2;
unsigned char c;
int isok;
unsigned char seq[2], seq2;
lock->unlock();
nread = ::read(fd,&c,1);
if(!read_char(c))
{
lock->lock();
return -2;
}
lock->lock();
if (nread <= 0) return raw_buffer.size();
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
@ -429,7 +435,7 @@ namespace DFHack
switch(c)
{
case 13: // enter
history.pop_front();
history.remove();
return raw_buffer.size();
case 3: // ctrl-c
errno = EAGAIN;
@ -445,10 +451,10 @@ namespace DFHack
break;
case 27: // escape sequence
lock->unlock();
if (::read(fd,seq,2) == -1)
if(!read_char(seq[0]) || !read_char(seq[1]))
{
lock->lock();
break;
return -2;
}
lock->lock();
if(seq[0] == '[')
@ -513,10 +519,10 @@ namespace DFHack
{
// extended escape
lock->unlock();
if (::read(fd,&seq2,1) == -1)
if(!read_char(seq2))
{
lock->lock();
return -1;
return -2;
}
lock->lock();
if (seq[1] == '3' && seq2 == '~' )
@ -579,7 +585,6 @@ namespace DFHack
return raw_buffer.size();
}
FILE * dfout_C;
std::deque <std::string> history;
bool supported_terminal;
// state variables
bool rawmode; // is raw mode active?
@ -593,6 +598,9 @@ namespace DFHack
std::string prompt; // current prompt string
std::string raw_buffer; // current raw mode buffer
int raw_cursor; // cursor position in the buffer
// thread exit mechanism
int exit_pipe[2];
fd_set descriptor_set;
};
}
@ -629,6 +637,11 @@ bool Console::init(bool sharing)
std::cin.tie(this);
clear();
d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO);
// init the exit mechanism
pipe(d->exit_pipe);
FD_ZERO(&d->descriptor_set);
FD_SET(STDIN_FILENO, &d->descriptor_set);
FD_SET(d->exit_pipe[0], &d->descriptor_set);
inited = true;
}
@ -641,6 +654,8 @@ bool Console::shutdown(void)
d->disable_raw();
d->print("\n");
inited = false;
// kill the thing
close(d->exit_pipe[1]);
return true;
}
@ -727,20 +742,12 @@ void Console::cursor(bool enable)
d->cursor(enable);
}
// push to front, remove from back if we are above maximum. ignore immediate duplicates
void Console::history_add(const std::string & command)
{
lock_guard <mutex> g(*wlock);
if(inited)
d->history_add(command);
}
int Console::lineedit(const std::string & prompt, std::string & output)
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{
lock_guard <mutex> g(*wlock);
int ret = -2;
if(inited)
ret = d->lineedit(prompt,output,wlock);
ret = d->lineedit(prompt,output,wlock,ch);
return ret;
}

@ -250,7 +250,7 @@ namespace DFHack
SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
}
int prompt_loop(mutex * lock)
int prompt_loop(mutex * lock, CommandHistory & history)
{
raw_buffer.clear(); // make sure the buffer is empty!
size_t plen = prompt.size();
@ -259,7 +259,7 @@ namespace DFHack
// The latest history entry is always our current buffer, that
// initially is just an empty string.
const std::string empty;
history_add(empty);
history.add(empty);
CONSOLE_SCREEN_BUFFER_INFO inf = { 0 };
GetConsoleScreenBufferInfo(console_out, &inf);
@ -280,7 +280,7 @@ namespace DFHack
switch (rec.Event.KeyEvent.wVirtualKeyCode)
{
case VK_RETURN: // enter
history.pop_front();
history.remove();
return raw_buffer.size();
case VK_BACK: // backspace
if (raw_cursor > 0 && raw_buffer.size() > 0)
@ -359,13 +359,13 @@ namespace DFHack
}
}
}
int lineedit(const std::string & prompt, std::string & output, mutex * lock)
int lineedit(const std::string & prompt, std::string & output, mutex * lock, CommandHistory & ch)
{
output.clear();
int count;
state = con_lineedit;
this->prompt = prompt;
count = prompt_loop(lock);
count = prompt_loop(lock, ch);
if(count != -1)
output = raw_buffer;
state = con_unclaimed;
@ -373,21 +373,8 @@ namespace DFHack
return count;
}
// push to front, remove from back if we are above maximum. ignore immediate duplicates
void history_add(const std::string & command)
{
// if current command = last in history -> do not add. Always add if history is empty.
if(!history.empty() && history.front() == command)
return;
history.push_front(command);
if(history.size() > 100)
history.pop_back();
}
FILE * dfout_C;
int rawmode;
std::deque <std::string> history;
HANDLE console_in;
HANDLE console_out;
HWND ConsoleWindow;
@ -556,20 +543,12 @@ void Console::cursor(bool enable)
d->cursor(enable);
}
// push to front, remove from back if we are above maximum. ignore immediate duplicates
void Console::history_add(const std::string & command)
{
lock_guard <mutex> g(*wlock);
if(inited)
d->history_add(command);
}
int Console::lineedit(const std::string & prompt, std::string & output)
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{
wlock->lock();
int ret = -2;
if(inited)
ret = d->lineedit(prompt,output,wlock);
ret = d->lineedit(prompt,output,wlock,ch);
wlock->unlock();
return ret;
}

@ -133,6 +133,8 @@ void fIOthread(void * iodata)
IODATA * iod = ((IODATA*) iodata);
Core * core = iod->core;
PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
CommandHistory main_history;
main_history.load("dfhack.history");
Console & con = core->con;
if(plug_mgr == 0 || core == 0)
{
@ -145,7 +147,7 @@ void fIOthread(void * iodata)
while (true)
{
string command = "";
int ret = con.lineedit("[DFHack]# ",command);
int ret = con.lineedit("[DFHack]# ",command, main_history);
if(ret == -2)
{
cerr << "Console is shutting down properly." << endl;
@ -159,7 +161,8 @@ void fIOthread(void * iodata)
else if(ret)
{
// a proper, non-empty command was entered
con.history_add(command);
main_history.add(command);
main_history.save("dfhack.history");
}
// cut the input into parts
vector <string> parts;
@ -387,7 +390,7 @@ Core::Core()
hotkey_set = false;
HotkeyMutex = 0;
HotkeyCond = 0;
misc_data_mutex=0;
misc_data_mutex=0;
};
bool Core::Init()

@ -25,7 +25,12 @@ distribution.
#pragma once
#include "dfhack/Pragma.h"
#include "dfhack/Export.h"
#include <ostream>
#include <deque>
#include <fstream>
#include <llimits.h>
#include <assert.h>
#include <iostream>
#include <string>
namespace tthread
{
class mutex;
@ -34,6 +39,74 @@ namespace tthread
}
namespace DFHack
{
class CommandHistory
{
public:
CommandHistory(std::size_t capacity = 100)
{
this->capacity = capacity;
}
bool load (const char * filename)
{
std::string reader;
std::ifstream infile(filename);
if(infile.bad())
return false;
std::string s;
while(std::getline(infile, s))
{
if(s.empty())
continue;
history.push_back(s);
}
return true;
}
bool save (const char * filename)
{
std::ofstream outfile (filename);
if(outfile.bad())
return false;
for(auto iter = history.begin();iter < history.end(); iter++)
{
outfile << *iter << std::endl;
}
outfile.close();
return true;
}
/// add a command to the history
void add(const std::string& command)
{
// if current command = last in history -> do not add. Always add if history is empty.
if(!history.empty() && history.front() == command)
return;
history.push_front(command);
if(history.size() > capacity)
history.pop_back();
}
/// clear the command history
void clear()
{
history.clear();
}
/// get current history size
std::size_t size()
{
return history.size();
}
/// get pointer to a particular history item
std::string & operator[](std::size_t index)
{
assert(index < history.size());
return history[index];
}
void remove( void )
{
history.pop_front();
}
private:
std::size_t capacity;
std::deque <std::string> history;
};
class Private;
class DFHACK_EXPORT Console : public std::ostream
{
@ -91,14 +164,10 @@ namespace DFHack
/// beep. maybe?
//void beep (void);
/// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output);
/// add a command to the history
void history_add(const std::string& command);
/// clear the command history
void history_clear();
int lineedit(const std::string& prompt, std::string& output, CommandHistory & history );
private:
Private * d;
tthread::mutex * wlock;
bool inited;
};
}
}

@ -15,7 +15,7 @@
#include "lua_Console.h"
#include "lua_Process.h"
#include "lua_Hexsearch.h"
#include "lua_Misc.h"
#include "lua_Misc.h"
#include "lua_VersionInfo.h"
#include "functioncall.h"
@ -41,7 +41,7 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand>
lua::RegisterConsole(lua::glua::Get(),&c->con);
lua::RegisterProcess(lua::glua::Get(),c->p);
lua::RegisterHexsearch(lua::glua::Get());
lua::RegisterMisc(lua::glua::Get());
lua::RegisterMisc(lua::glua::Get());
lua::RegisterVersionInfo(lua::glua::Get());
commands.push_back(PluginCommand("dfusion","Init dfusion system.",dfusion));
commands.push_back(PluginCommand("lua", "Run interactive interpreter.\
@ -89,13 +89,14 @@ DFhackCExport command_result plugin_onupdate ( Core * c )
void InterpreterLoop(Core* c)
{
Console &con=c->con;
DFHack::CommandHistory hist;
lua::state s=lua::glua::Get();
string curline;
con.print("Type quit to exit interactive mode\n");
con.lineedit(">>",curline);
con.lineedit(">>",curline,hist);
while (curline!="quit") {
con.history_add(curline);
hist.add(curline);
try
{
s.loadstring(curline);
@ -104,10 +105,10 @@ void InterpreterLoop(Core* c)
catch(lua::exception &e)
{
con.printerr("Error:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()).c_str());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()).c_str());
s.settop(0);
}
con.lineedit(">>",curline);
con.lineedit(">>",curline,hist);
}
s.settop(0);
}
@ -125,7 +126,7 @@ DFhackCExport command_result lua_run (Core * c, vector <string> & parameters)
catch(lua::exception &e)
{
con.printerr("Error:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()).c_str());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()).c_str());
}
}
else
@ -150,7 +151,7 @@ DFhackCExport command_result dfusion (Core * c, vector <string> & parameters)
catch(lua::exception &e)
{
con.printerr("Error:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()).c_str());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()).c_str());
}
s.settop(0);// clean up
mymutex->unlock();

@ -88,25 +88,12 @@ static int lua_Console_lineedit(lua_State *S)
lua::state st(S);
DFHack::Console* c=GetConsolePtr(st);
string ret;
int i=c->lineedit(st.as<string>(1),ret);
DFHack::CommandHistory hist;
int i=c->lineedit(st.as<string>(1),ret,hist);
st.push(ret);
st.push(i);
return 2;// dunno if len is needed...
}
static int lua_Console_history_add(lua_State *S)
{
lua::state st(S);
DFHack::Console* c=GetConsolePtr(st);
c->history_add(st.as<string>(1));
return 0;
}
/*static int lua_Console_history_clear(lua_State *S) //TODO someday add this
{
lua::state st(S);
DFHack::Console* c=GetConsolePtr(st);
c->history_clear();
return 0;
}*/
const luaL_Reg lua_console_func[]=
{
{"print",lua_Console_print},
@ -120,8 +107,6 @@ const luaL_Reg lua_console_func[]=
{"get_columns",lua_Console_get_columns},
{"get_rows",lua_Console_get_rows},
{"lineedit",lua_Console_lineedit},
{"history_add",lua_Console_history_add},
//{"history_clear",lua_Console_history_clear},
{NULL,NULL}
};
void lua::RegisterConsole(lua::state &st, DFHack::Console *c)

@ -140,6 +140,8 @@ public:
};
};
CommandHistory liquids_hist;
DFhackCExport command_result df_liquids (Core * c, vector <string> & parameters);
DFhackCExport const char * plugin_name ( void )
@ -149,6 +151,7 @@ DFhackCExport const char * plugin_name ( void )
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
{
liquids_hist.load("liquids.history");
commands.clear();
commands.push_back(PluginCommand("liquids", "Place magma, water or obsidian.", df_liquids, true));
return CR_OK;
@ -156,6 +159,7 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand>
DFhackCExport command_result plugin_shutdown ( Core * c )
{
liquids_hist.save("liquids.history");
return CR_OK;
}
@ -192,7 +196,7 @@ DFhackCExport command_result df_liquids (Core * c, vector <string> & parameters)
string command = "";
std::stringstream str;
str <<"[" << mode << ":" << brushname << ":" << amount << ":" << flowmode << ":" << setmode << "]#";
if(c->con.lineedit(str.str(),command) == -1)
if(c->con.lineedit(str.str(),command,liquids_hist) == -1)
return CR_FAILURE;
if(command=="help" || command == "?")
{
@ -260,20 +264,24 @@ DFhackCExport command_result df_liquids (Core * c, vector <string> & parameters)
else if(command == "range" || command == "r")
{
std::stringstream str;
CommandHistory range_hist;
str << " :set range width<" << width << "># ";
c->con.lineedit(str.str(),command);
c->con.lineedit(str.str(),command,range_hist);
range_hist.add(command);
width = command == "" ? width : atoi (command.c_str());
if(width < 1) width = 1;
str.clear();
str << " :set range height<" << height << "># ";
c->con.lineedit(str.str(),command);
c->con.lineedit(str.str(),command,range_hist);
range_hist.add(command);
height = command == "" ? height : atoi (command.c_str());
if(height < 1) height = 1;
str.clear();
str << " :set range z-levels<" << z_levels << "># ";
c->con.lineedit(str.str(),command);
c->con.lineedit(str.str(),command,range_hist);
range_hist.add(command);
z_levels = command == "" ? z_levels : atoi (command.c_str());
if(z_levels < 1) z_levels = 1;
delete brush;

@ -131,7 +131,8 @@ DFhackCExport command_result mode (Core * c, vector <string> & parameters)
string selected;
input_again:
c->con.lineedit("Enter new mode: ",selected);
CommandHistory hist;
c->con.lineedit("Enter new mode: ",selected, hist);
if(selected == "c")
return CR_OK;
const char * start = selected.c_str();

@ -411,6 +411,8 @@ public:
};
};
CommandHistory tiletypes_hist;
DFhackCExport command_result df_tiletypes (Core * c, vector <string> & parameters);
DFhackCExport const char * plugin_name ( void )
@ -420,6 +422,7 @@ DFhackCExport const char * plugin_name ( void )
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
{
tiletypes_hist.load("tiletypes.history");
commands.clear();
commands.push_back(PluginCommand("tiletypes", "Paint map tiles freely, similar to liquids.", df_tiletypes, true));
return CR_OK;
@ -427,6 +430,7 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand>
DFhackCExport command_result plugin_shutdown ( Core * c )
{
tiletypes_hist.save("tiletypes.history");
return CR_OK;
}
@ -455,7 +459,8 @@ DFhackCExport command_result df_tiletypes (Core * c, vector <string> & parameter
std::string option = "";
std::string value = "";
c->con.lineedit("tiletypes> ",input);
c->con.lineedit("tiletypes> ",input,tiletypes_hist);
tiletypes_hist.add(input);
std::istringstream ss(input);
ss >> command >> option >> value;
tolower(command);
@ -486,20 +491,21 @@ DFhackCExport command_result df_tiletypes (Core * c, vector <string> & parameter
else if (command == "range" || command == "r")
{
std::stringstream ss;
CommandHistory hist;
ss << "Set range width <" << width << "> ";
c->con.lineedit(ss.str(),command);
c->con.lineedit(ss.str(),command,hist);
width = command == "" ? width : toint(command);
if (width < 1) width = 1;
ss.str("");
ss << "Set range height <" << height << "> ";
c->con.lineedit(ss.str(),command);
c->con.lineedit(ss.str(),command,hist);
height = command == "" ? height : toint(command);
if (height < 1) height = 1;
ss.str("");
ss << "Set range z-levels <" << z_levels << "> ";
c->con.lineedit(ss.str(),command);
c->con.lineedit(ss.str(),command,hist);
z_levels = command == "" ? z_levels : toint(command);
if (z_levels < 1) z_levels = 1;

@ -23,10 +23,7 @@ DFhackCExport const char * plugin_name ( void )
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
{
commands.clear();
commands.push_back(PluginCommand("weather",
"Print the weather map or change weather.\
\n Options: 'snow' = make it snow, 'rain' = make it rain.\
\n 'clear' = clear the sky",weather));
commands.push_back(PluginCommand("weather", "Print the weather map or change weather.",weather));
return CR_OK;
}
@ -61,7 +58,13 @@ DFhackCExport command_result weather (Core * c, vector <string> & parameters)
}
if(help)
{
c->con.print("Prints the current weather map by default.\n"
"Options:\n"
"snow - make it snow everywhere.\n"
"rain - make it rain.\n"
"clear - clear the sky.\n"
);
return CR_OK;
}
if(lock && unlock)
{