Scite 宏扩展器 |
|
我一直很喜欢 C 预处理器宏功能。在我看来,这个宏使 C 代码更易读,更不容易出错
#define FOR(i,n) for(i = 0; i < (n); i++)
不幸的是,由于滥用,语句宏的规范使用已经不受欢迎,C++ 风格指南通常反对使用它们(Stroustrup 总是采取更务实的立场)
SciTE Lua 宏扩展功能是一个简单的宏预处理器,它在当前缓冲区中扩展宏,有点像缩写。例如,我可以将以下内容放在我的属性文件中
macro.subst.1=for(i,n)=for(i = 0; i < n; i++)
并在缓冲区中键入 FOR(k,10)
,然后按“扩展宏”(Alt+Enter)。一个简单的单遍扩展将发生,并且 for(k = 0; k < 10; k++)
将替换缓冲区中的宏。
由于它是一个单遍扩展,并且永远不会自动发生,因此在这里使用像“for”这样的关键字没有问题。
换行符在宏定义中用“\n”表示,你始终可以使用反斜杠将定义拆分为多行。
macro.subst.2=if(cond)=if(cond) { \n } else { \n }
与缩写不同,扩展后的代码不会自动缩进。将缩写使用的自动格式化代码公开并不难,但在那之前,我们的想法是使用标准版本。
这个宏扩展器最有趣的部分是它可以很容易地使用 Lua 扩展。在你的宏中出现的以 $ 开头的任何函数调用都被认为是调用全局定义的 Lua 函数。伪函数“eval”始终可用,它允许你将 Lua 表达式直接放入宏中
macro.subst.3=date=$eval(os.date())
这个宏没有参数,并在扩展时将当天日期的合理表示插入到你的当前文件中。
我已经定义了 $cat
来执行“令牌粘贴”,以及 $quote
来执行“字符串化”,而无需使用 # 和 ##。假设
newfn(name)=void $cat(INIT_,name) (int i) {\n}\n
那么 newfn'first'
将扩展为
void INIT_first(int i) { }
请注意,你可以使用引号(单引号或双引号)将一个参数传递给宏,而不是传统的圆括号。
你不必指定宏的所有参数;如果参数未使用,则假定它们为 nil。这在将参数传递给 Lua 函数时非常方便。
将 Files:wiki_insecure/editors/SciTE/macro.lua 的内容放在你现有的 Lua 启动文件中,或者直接使用以下命令加载它
ext.lua.startup.script=$(SciteDefaultHome)/macro.lua
这需要一个绝对路径 - SciTE 属性在这里很有用!
在您的属性文件中,将两个键绑定到函数 do_macro 和 macro_select(可以根据您的喜好更改它们,但请记住您不能覆盖 SciTE 定义的快捷键)。command.mode 行是必要的,以防止那些令人讨厌的“您要保存吗”消息。
command.name.11.*=Expand Macro command.11.*=do_macro command.subsystem.11.*=3 command.mode.11.*=savebefore:no command.shortcut.11.*=Alt+Enter command.name.12.*=Macro Select command.12.*=macro_select command.subsystem.12.*=3 command.mode.12.*=savebefore:no command.shortcut.12.*=Ctrl+=
现在您可以定义一些宏(它们可以放在任何加载的属性文件中)
macro.subst.1=for(i,n)=for(i = 0; i < n; i++) macro.subst.2=if(cond)=if(cond) { \n \ \n \ } else { \n \ \n \ } macro.subst.3=test(xx)=$quote(xx) macro.subst.4=date=$eval(os.date()) macro.subst.5=class(name,base)=$do_class(name,base) macro.subst.6=insert(x)=$insert_file(x) macro.subst.7=i(x)=$international_string(x) macro.subst.8=_(x)=$upcase(x)
这里我们利用了这样一个事实,即如果宏参数未指定,则它将被视为 nil。
class(name,base)=$do_class(name,base)
其中
function do_class(name,base) local res = 'class '..name if base then res = res..': public '..base end return res..' {\n public:\n};\n' end
现在扩展 class(Dog,Animal)
将为您提供
class Dog: public Animal { public: };
扩展 class(Device)
将为您提供
class Device { public: };
有时有用,但只是偶尔有用,因此不值得定义一个正常的命令。此外,通常在 Lua 中定义的命令无法提示用户输入。(也许这将是一个有用的功能,但这是现有 1.6.1 版本可以做到的)。
要使用,请键入 insert"file"
并使用 Alt+Enter 扩展宏。
... insert(x)=$insert_file(x) function insert_file(file) local f = io.open(file) local txt = f:read('*a') if txt then f:close() return txt else return "" end end
我们希望编写一个可国际化的应用程序,但希望避免所有繁琐的定义。
这是一个宏的示例,它除了实际插入到程序中的文本之外,还有更新头文件的副作用。
这是一个展示解决此问题的一种方法的宏
i(x)=$international_string(x)
其中 Lua 函数为
function international_string(x) local id = convert_to_iden(x) local f = io.open('international.h','a') f:write('#define '..id..' '..quote(x)..'\n') f:close() return id end
我们需要将字符串转换为 C 风格的标识符,方法是将所有非法字符替换为下划线,并将结果修剪到某个最大长度。
function convert_to_iden(x) local s = string.upper(x) s = string.gsub(s,'[^%w_]','_') return string.sub(s,1,32) end
要使用它,请键入以“i”开头的字符串,然后按 Alt+Enter 调用宏:MessageBox(i"Please insert a disk"
将变为 MessageBox(PLEASE_INSERT_A_DISK
,并且 'international.h' 将自动更新!当然,您可以通过使用 props['InternationalHeader?'] 并将名称定义为属性来轻松自定义此名称。
任何名称为“_”的宏都有特殊含义;它将由当前选定文本上的“对选择操作”调用。
_(x)=$upcase(x)
其中 Lua 为
function upcase(x) return string.upper(x) end
Ctrl+= 现在将转换选定的文本。
“对选择操作”机制似乎很死板,因为一次只能使用一个操作。以下是一种动态重新定义此宏的技术。添加此宏
def(op)=$add_macro(x)
然后,您可以通过扩展 def 在任何时候添加宏。例如,扩展 def'_(x)=$quote(x)
将更改选择操作为引用。
这是我做过数百次的事情,但一点自动化可以使这项繁琐的任务变得容易。
include(file)=$create_include(file) function create_include(file) local id = '__'..convert_to_iden(file) local path local find = string.find -- is it absolute? If not, then prepend the current file's path if find(file,'^[/\\]') or find(file,'^%a:') then path = file else path = props['FileDir']..'/'..f end local f = io.open(path,'w') local copyright = props['CopyrightNotice'] if copyright then f:write(copyright) end f:write '\n' f:write ('#ifndef '..id..'\n') f:write ('#define '..id..'\n\n') f:write ('#endif\n') f:close() return '#include '..quote(file) end
其中 convert_to_iden
是之前定义的,quote
是由 Lua Macro 包定义的。一个明显的改进是检查文件是否已存在,并提供有关创建文件位置的更多灵活性。插入 #include 后,您可以使用打开选定文件名(Ctrl+Shift+O)打开文件。
扩展 Enter 功能并不困难,可以使用 OnKey
(参见 Sebastian 的 SciteLatex 页面,其中有示例)。甚至可以通过查看输入的单词来自动扩展宏(例如,参见 SciteWordSubstitution),但这可能会让人感到有些意外。(当然,如果遇到意外扩展,Ctrl+Z 始终是你的好朋友。)如果插入的文本能够遵循当前语言的缩进规则,比如缩写,那就太酷了,但这需要对 SciTE 本身进行一些更改,而且可能并不总是需要(所以问题是:如何指示需要智能插入?)。
记住所有定义的宏并不总是容易,因此下拉列表会很有用。使用 editor:UserListShow
和 OnUserListSelection
来实现并不困难;如果宏不接受参数,则选择它会立即扩展它。
对我来说,最令人兴奋和开放的特性是能够“内联”执行任意 Lua 代码,这仅受我们创造力的限制。