Files
textmate/Frameworks/command/src/parser.cc
Allan Odgaard 0fd03842b7 Move bundle requirement checking to bundles framework
This also generalizes it to being applicable to all bundle items (not just commands).
2013-05-09 15:16:33 +07:00

154 lines
5.9 KiB
C++

#include "parser.h"
#include <bundles/bundles.h>
template <typename T>
T pick (std::string const& value, ...)
{
va_list ap;
va_start(ap, value);
size_t i = 0;
while(char const* str = va_arg(ap, char const*))
{
if(value == str)
return T(i);
++i;
}
va_end(ap);
return T(0);
}
plist::dictionary_t convert_command_from_v1 (plist::dictionary_t plist)
{
int32_t version = 0;
if(plist::get_key_path(plist, "version", version) && version != 1)
return plist;
/*
replaceSelectedText || replaceSelection || insertAsText
→ placement: replace input
→ caret: if hasSelection && input is selection: select output
→ caret: if !hasSelection && fallback input is line, scope, or document: interpolate by char
→ caret: else: after output
replaceDocument
→ placement: replace document
→ caret: interpolate (line number)
afterSelectedText, with input set to “selection or «unit»”
→ placement: after input
→ caret: after output
afterSelectedText, with input set to “none” or “document”
→ placement: after selection (at caret)
→ caret: after output
insertAsSnippet, with input set to “selection or «unit»”
→ replace input
insertAsSnippet, with input set to “none” or “document”
→ replace selection
*/
static struct { char const* oldOutput; std::string const output; std::string const format; std::string const caret; } conversionMap[] =
{
{ "discard", "discard", "text", NULL_STR },
{ "replaceSelectedText", "replaceInput", "text", "heuristic" },
{ "replaceSelection", "replaceInput", "text", "heuristic" },
{ "insertAsText", "replaceInput", "text", "heuristic" },
{ "replaceDocument", "replaceDocument", "text", "interpolateByLine" },
{ "afterSelectedText", "afterInput", "text", "afterOutput" },
{ "insertAsSnippet", "replaceInput", "snippet", NULL_STR },
{ "showAsHTML", "newWindow", "html", NULL_STR },
{ "showAsTooltip", "toolTip", "text", NULL_STR },
{ "openAsNewDocument", "newWindow", "text", NULL_STR },
};
std::string oldOutput;
if(plist::get_key_path(plist, "output", oldOutput))
{
for(size_t i = 0; i < sizeofA(conversionMap); ++i)
{
if(oldOutput == conversionMap[i].oldOutput)
{
plist["outputLocation"] = conversionMap[i].output;
plist["outputFormat"] = conversionMap[i].format;
if(conversionMap[i].caret != NULL_STR)
plist["outputCaret"] = conversionMap[i].caret;
std::string input;
if(oldOutput == "afterSelectedText" && plist::get_key_path(plist, "input", input) && input != "selection")
plist["outputLocation"] = std::string("atCaret");
else if(oldOutput == "insertAsSnippet" && plist::get_key_path(plist, "input", input) && input != "selection")
plist["outputLocation"] = std::string("replaceSelection");
break;
}
}
}
bool flag;
plist["autoScrollOutput"] = plist::get_key_path(plist, "dontFollowNewOutput", flag) && !flag || plist::get_key_path(plist, "autoScrollOutput", flag) && flag;
return plist;
}
static void setup_fields (plist::dictionary_t const& plist, bundle_command_t& res)
{
std::string scopeSelectorString, preExecString, inputString, inputFormatString, inputFallbackString, outputFormatString, outputLocationString, outputCaretString;
plist::get_key_path(plist, "name", res.name);
plist::get_key_path(plist, "uuid", res.uuid);
plist::get_key_path(plist, "command", res.command);
if(plist::get_key_path(plist, "scope", scopeSelectorString))
res.scope_selector = scopeSelectorString;
if(plist::get_key_path(plist, "beforeRunningCommand", preExecString))
res.pre_exec = pick<pre_exec::type>(preExecString, "nop", "saveActiveFile", "saveModifiedFiles", NULL);
if(plist::get_key_path(plist, "inputFormat", inputFormatString))
res.input_format = pick<input_format::type>(inputFormatString, "text", "xml", NULL);
if(plist::get_key_path(plist, "input", inputString))
res.input = pick<input::type>(inputString, "selection", "document", "scope", "line", "word", "character", "none", NULL);
if(plist::get_key_path(plist, "fallbackInput", inputFallbackString))
res.input_fallback = pick<input::type>(inputFallbackString, "selection", "document", "scope", "line", "word", "character", "none", NULL);
if(plist::get_key_path(plist, "outputFormat", outputFormatString))
res.output_format = pick<output_format::type>(outputFormatString, "text", "snippet", "html", "completionList", NULL);
if(plist::get_key_path(plist, "outputLocation", outputLocationString))
res.output = pick<output::type>(outputLocationString, "replaceInput", "replaceDocument", "atCaret", "afterInput", "newWindow", "toolTip", "discard", "replaceSelection", NULL);
if(plist::get_key_path(plist, "outputCaret", outputCaretString))
res.output_caret = pick<output_caret::type>(outputCaretString, "afterOutput", "selectOutput", "interpolateByChar", "interpolateByLine", "heuristic", NULL);
plist::get_key_path(plist, "autoScrollOutput", res.auto_scroll_output);
plist::get_key_path(plist, "disableOutputAutoIndent", res.disable_output_auto_indent);
}
bundle_command_t parse_command (plist::dictionary_t const& plist)
{
bundle_command_t res = { };
res.input_fallback = input::entire_document;
setup_fields(plist, res);
return res;
}
bundle_command_t parse_command (bundles::item_ptr bundleItem)
{
return parse_command(convert_command_from_v1(bundleItem->plist()));
}
bundle_command_t parse_drag_command (bundles::item_ptr bundleItem)
{
bundle_command_t res = { };
res.input = res.input_fallback = input::nothing;
res.output = output::at_caret;
res.output_format = output_format::snippet;
plist::dictionary_t const& plist = bundleItem->plist();
setup_fields(plist, res);
return res;
}