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:UserListShowOnUserListSelection 来实现并不困难;如果宏不接受参数,则选择它会立即扩展它。

对我来说,最令人兴奋和开放的特性是能够“内联”执行任意 Lua 代码,这仅受我们创造力的限制。

SteveDonovan


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2007 年 3 月 7 日凌晨 2:43 GMT (差异)