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 代码的能力,这仅受限于我们的创造力。