Dear all,

At last I had time and play with the insetcommandparams as requested
on nomencl discussion. The new implementation keeps the contents and
options in lists. There is also support for individual parameters with
std::multimaps for future implementations. I haven't put any kind of
nested command support, since it would be much easier when we migrate
to the XML format.

After the development, the scanCommand() becomes obsolete. However,
insetBibitem uses scan command directly. Therefore, I will keep it
there until the development approved. Then I can update insetBibitem
to use new insetcommandparams.

Well.. I hope what I have done here complies with what you asked for.. :)

ugras
/**
 * \file insetcommandparams.C
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Angus Leeming
 *
 * Full author contact details are available in file CREDITS.
 */

#include <config.h>

#include "insetcommandparams.h"

#include "debug.h"
#include "lyxlex.h"


using std::string;
using std::pair;

InsetCommandParams::InsetCommandParams()
{}


InsetCommandParams::InsetCommandParams(string const & n,
					string const & c,
					string const & o,
					string const & s,
					string const & sc)
	: cmdname(n), //contents(c), options(o), sec_options(s), sec_contents(sc),
	preview_(false)
{
	if(!c.empty()) setContents(c);
	if(!sc.empty()) setSecContents(sc);
	if(!o.empty()) setOptions(o);
	if(!s.empty()) setSecOptions(s);
}

InsetCommandParams::InsetCommandParams(string const & n,
					vector<string> const & c,
					vector<string> const & o)
	: cmdname(n), contents_(c) , options_(o),
	preview_(false)
{
//TODO never tested, but should work!! :/
}

//TODO apparently this function is only needed for insetBibItem.. remove after fix..
void InsetCommandParams::scanCommand(string const & cmd)
{
	string tcmdname, toptions, tsecoptions, tcontents;

	if (cmd.empty()) return;

	enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;

	// Used to handle things like \command[foo[bar]]{foo{bar}}
	int nestdepth = 0;

	for (string::size_type i = 0; i < cmd.length(); ++i) {
		char const c = cmd[i];
		if ((state == CMDNAME && c == ' ') ||
		    (state == CMDNAME && c == '[') ||
		    (state == CMDNAME && c == '{')) {
			state = WS;
		}
		if ((state == OPTION  && c == ']') ||
		    (state == SECOPTION  && c == ']') ||
		    (state == CONTENT && c == '}')) {
			if (nestdepth == 0) {
				state = WS;
			} else {
				--nestdepth;
			}
		}
		if ((state == OPTION  && c == '[') ||
		    (state == SECOPTION  && c == '[') ||
		    (state == CONTENT && c == '{')) {
			++nestdepth;
		}
		switch (state) {
		case CMDNAME:	tcmdname += c; break;
		case OPTION:	toptions += c; break;
		case SECOPTION:	tsecoptions += c; break;
		case CONTENT:	tcontents += c; break;
		case WS: {
			char const b = i? cmd[i-1]: 0;
			if (c == '\\') {
				state = CMDNAME;
			} else if (c == '[' && b != ']') {
				state = OPTION;
				nestdepth = 0; // Just to be sure
			} else if (c == '[' && b == ']') {
				state = SECOPTION;
				nestdepth = 0; // Just to be sure
			} else if (c == '{') {
				state = CONTENT;
				nestdepth = 0; // Just to be sure
			}
			break;
		}
		}
	}

	// Don't mess with this.
	if (!tcmdname.empty())  setCmdName(tcmdname);
	if (!toptions.empty())  setOptions(toptions);
	if (!tsecoptions.empty())  setSecOptions(tsecoptions);
	if (!tcontents.empty()) setContents(tcontents);

	if (lyxerr.debugging(Debug::PARSER))
		lyxerr << "Command <" <<  cmd
		       << "> == <" << getCommand()
		       << "> == <" << getCmdName()
		       << '|' << getContents()
		       << '|' << getOptions()
		       << '|' << getSecOptions() << '>' << endl;
}



void InsetCommandParams::read(LyXLex & lex)
{
	string token;
///////////////////////////////////////////////////////////////////////
// left for compatibility issues. remove if necessary!!
	if (lex.eatLine()) {
		token = lex.getString();
		//scanCommand(token);
	} else {
		lex.printError("InsetCommand: Parse error: `$$Token'");
	}
///////////////////////////////////////////////////////////////////////
	options_.clear();
	contents_.clear();

	while (lex.isOK()) {
		lex.next();
		token = lex.getString();
		if (token == "\\end_inset")
			break;
		if (token == "\\command_name") {
			lex.eatLine();
			setCmdName(clearText(lex.getString()));
		}
		if (token == "\\add_contents") {
			lex.eatLine();
			addContents(clearText(lex.getString()));
		}
		if (token == "\\add_options") {
			lex.eatLine();
			addOptions(clearText(lex.getString()));
		}
		if (hasKey(token)) {
			lex.eatLine();
			entrymap_.insert(pair<string,string>(token,clearText(lex.getString())));
		}		
		if (token == "preview") {
			lex.next();
			preview_ = lex.getBool();
		}
	}
	if (token != "\\end_inset") {
		lex.printError("Missing \\end_inset at this point. "
		"Read: `$$Token'");
	}
}


// it is necessary to put the input into {}, since some options or contents 
// should be set even if it is null..
// nest depth stuff that was introduced in scanCommand should be avoided,
// since user can enter an erroneous input like foo{boo . 
// It is not necessary to mix up things even more! :)
string InsetCommandParams::clearText(string const & cmd)
{
   return string(cmd,
		cmd.find_first_not_of('{' , 0         ),
		cmd.find_last_not_of ('}' , cmd.size()));
}

void InsetCommandParams::write(ostream & os) const
{
unsigned int ii;
	os << "LatexCommand " << getCommand() << "\n";
	os << "\\command_name " << '{' << getCmdName() << '}' << "\n";
	for( ii = 0 ; ii < options_.size() ; ++ii )
	os << "\\add_options " << '{' <<  getOptions(ii) << '}' <<  "\n";
	for( ii = 0 ; ii < contents_.size() ; ++ii )
	os << "\\add_contents " << '{' <<  getContents(ii)  << '}' <<  "\n";
}



string const InsetCommandParams::getCommand() const
{
	string s;
	unsigned int ii;
	if (!getCmdName().empty()) s += '\\' + getCmdName();
	for( ii = 0 ; ii < options_.size() ; ++ii ) s += '[' + getOptions(ii) + ']';
	if (getNumContents() == 0) s += "{}";
	for( ii = 0 ; ii < contents_.size() ; ++ii ) s += '{' + getContents(ii) + '}';
	return s;
}

bool operator==(InsetCommandParams const & o1,
		InsetCommandParams const & o2)
{
	bool isequal = (o1.getNumOptions() == o2.getNumOptions()) &&
			(o1.getNumContents() == o2.getNumContents());
	int ii;
	for( ii = 0 ; ii < o1.getNumOptions() && isequal ; ++ii ) 
		isequal = o1.getOptions(ii) == o2.getOptions(ii);
	for( ii = 0 ; ii < o1.getNumContents() && isequal ; ++ii ) 
	isequal = o1.getContents(ii) == o2.getContents(ii);
	return isequal && (o1.getCmdName() == o2.getCmdName());
}


bool operator!=(InsetCommandParams const & o1,
		InsetCommandParams const & o2)
{
	return !(o1 == o2);
}
// -*- C++ -*-
/**
 * \file insetcommandparams.h
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Angus Leeming
 *
 * Full author contact details are available in file CREDITS.
 */

#ifndef INSETCOMMANDPARAMS_H
#define INSETCOMMANDPARAMS_H

#include <string>
#include <iosfwd>
#include <vector>
#include <map>

#include "debug.h"

class LyXLex;
using std::string;
using std::endl;
using std::ostream;
using std::vector;
using std::map;

class InsetCommandParams {
public:
	///
	InsetCommandParams();
	///
	explicit InsetCommandParams(std::string const & n,
			std::string const & c = std::string(),
			std::string const & o = std::string(),
			std::string const & s = std::string(),
			std::string const & sc = std::string());
	explicit InsetCommandParams(std::string const & n,
			std::vector<std::string> const & c,
			std::vector<std::string> const & o);
	///
	void read(LyXLex &);
	///Parse the command
	void scanCommand(std::string const &);
	///
	void write(std::ostream &) const;
	///
	std::string const & getCmdName() const { return cmdname; }
	///
	std::string const & getOptions() const { return (options_.size())?options_[0]:null_str; }
	///
	std::string const & getSecOptions() const { return (options_.size()>1)?options_[1]:null_str; }
	///
	std::string const & getContents() const { return (contents_.size() )?contents_[0]:null_str; }
	///
	std::string const & getSecContents() const { return (contents_.size() >1)?contents_[1]:null_str; }
	///
	int getNumOptions() const { return options_.size(); }
	///
	int getNumContents() const { return contents_.size(); }
	///
	void setCmdName(std::string const & n) { cmdname = n; }
	///
	void setOptions(std::string  const & c){ setElement(options_, 0, c);}
	///
	void setSecOptions(std::string const & c) { setElement(options_, 1, c);}
	///
	void setContents(std::string const & c) { setElement(contents_, 0, c);}
	///
	void setSecContents(std::string const & s) { setElement(contents_, 1, s);}
	///
	bool preview() const { return preview_; }
	///
	void preview(bool p) { preview_ = p; }
	///
	void addOptions(std::string const & o) { options_.push_back(o); }
	///
	void addContents(std::string const & c) { contents_.push_back(c); }
	///
	std::string const & getOptions(int i) const { return options_[i]; }
	///
	std::string const & getContents(int i) const { return contents_[i]; }
	///
	std::vector<std::string> getValuesForKeys(std::string const & v) /*TODO*/;
	///
	std::string getValueForKeys(std::string const & v) {return (*(std::multimap<std::string,std::string>::iterator(entrymap_.find(v)))).first;}
	///
	/// Build the complete LaTeX command
	std::string const getCommand() const;


private:
	///
	std::string clearText(std::string const &);
	///
	bool hasKey(std::string const & k) {return !(find(keys_.begin() , keys_.end() ,k) == keys_.end());};
	///
	void setElement(std::vector<std::string> & , int , std::string const & c);
	///
	std::string cmdname;
	///
	std::string null_str;
	///
	bool preview_;
	///
	std::vector<std::string> contents_;
	///
	std::vector<std::string> options_;
	///
	std::vector<std::string> keys_;
	///
	std::multimap<std::string,std::string> entrymap_;

};

///
inline void InsetCommandParams::setElement(std::vector<std::string> & vec_ , int ii, std::string const & c) 
{
	for (int i = vec_.size() ; i <= ii ; ++i ) {
		vec_.push_back("");
	}
	vec_[ii] = c;
}
///
bool operator==(InsetCommandParams const &, InsetCommandParams const &);

///
bool operator!=(InsetCommandParams const &, InsetCommandParams const &);

#endif

Reply via email to