Scite 宏扩展器

lua-users home
wiki

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)

示例

编写 C++ 类主体

这里我们利用了一个事实:如果宏参数未被指定,它就被视为 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) 将会把选择操作更改为引用。

自动创建 C/C++ 头文件

这是我做了上百次的事情,但一点自动化就能让这项繁琐的任务变得轻松。

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

SteveDonovan


RecentChanges · preferences
编辑 · 历史
最后编辑于 2007 年 3 月 6 日下午 8:43 GMT (差异)