使用 Lua 与 Scite

lua-users home
wiki

任何全局 Lua 函数都可以用作 SciTE 命令。这些函数会出现在工具菜单上,并且可能具有关联的快捷键。

以下是一个简单的示例;将此代码放入您的属性文件(可以是本地、用户或全局文件)

command.name.1.*=Load Lua
command.subsystem.1.*=3
command.1.*=dofile $(FilePath)

您现在将在工具菜单上有一个名为“加载 Lua”的菜单项,默认情况下它将具有快捷键 Ctrl+1。command.subsystem 必须设置为 3,这是 Lua 扩展。在这种情况下,全局 Lua 函数是 dofile,它被保留在 SciTE 的 Lua 5 中,正是因为它在这种情况下非常有用;请注意,dofile 后面没有参数列表。最多可以向您的函数传递一个参数,但与 SciTE 属性文件中的任何内容一样,它可以包含对其他 SciTE 属性的引用,例如 FilePath,它是当前显示文件的完整路径。(有关这些动态属性的完整列表,请参阅 SciTE 手册中的“属性文件”部分)

现在,如果您编辑包含以下内容的文件

  print ("Hello, World!")

并按下 Ctrl+1,则“Hello, World”将显示在 SciTE 输出窗口中。如果您修改了此文件,则 SciTE 会先提示您保存;这是默认行为。这是一种在 SciTE 环境中开始 Lua 编程的强大方法。

此定义存在一个潜在问题;其他一些脚本可能已经定义了命令 1;您可以使用此方法定义 0 到 9 的命令,绑定到 Ctrl+0 .. Ctrl+9。但是,您可以在这些定义中使用小于 50 的任何数字,只要您提供一个明确的快捷键即可

command.name.11.*=Load Lua
command.subsystem.11.*=3
command.11.*=dofile $(FilePath)
command.mode.11.*=savebefore:yes
command.shortcut.11.*=F9

可以重新绑定 SciTE 或 Scintilla 已经定义的键。有很多这样的键,因此请查阅文档。

以下是一种替代方法,它不需要将文档保存到磁盘 - 它会执行当前缓冲区中的所有源代码

command.name.1.*=Run Document as Lua Extension
command.subsystem.1.*=3
command.1.*=dostring dostring(editor:GetText())
command.mode.1.*=savebefore:no

一个简单的 SciTE 宏

将此代码放入您的主目录中的 Lua 文件(例如 test.lua)

function make_uppercase()
  local sel = editor:GetSelText()
  editor:ReplaceSel(string.upper(sel))
end

并在您的属性文件中,放入以下代码

ext.lua.startup.script=$(SciteUserHome)/test.lua

command.name.12.*=Make Selection Uppercase
command.subsystem.12.*=3
command.12.*=make_uppercase
command.mode.12.*=savebefore:no
command.shortcut.12.*=Ctrl+M

现在,在选择文本后,您可以使用 Ctrl+M 将其转换为大写。由于 savebefore:no,它不会在执行之前提示您保存。

好的,SciTE 确实已经有了这样的操作。但是您可以在 SciTE 中使用 Lua 做几乎任何事情!我建议学习的最佳方法是使用 dofile 技巧进行实验。

查找 Scintilla 函数的正确形式

Lua Scintilla 绑定的最佳参考是 scintilla.iface,它位于 Scintilla 包含目录中。如果您查找 GetLength,您会发现

 # Returns the number of characters in the document.
 get int GetLength=2006(,)

“get” 表示存在一个名为 Length 的只读属性。调用方式如下:editor.Length

而查找 GetText 时,我们得到

 # Retrieve all the text in the document.
 # Returns number of characters retrieved.
 fun int GetText=2182(int length, stringresult text)

因此 GetText 是一个普通的函数,它接受一个字符串作为参数。调用方式为 editor:GetText() - 注意冒号!

Lua 绑定并不总是保持一致,例如 GetSelText 是一个函数,而不是属性。

Lua 接口的 Scintilla 文档注释版本可以在这里找到:http://scite-interest.googlegroups.com/web/ScintillaSciteDoc.html

获取和修改文档文本

editor:GetText() 将返回当前文档的全部文本,而 editor:SetText(s) 将用字符串 s 替换文档的当前内容。

editor:GetLine(n) 将获取第 n 行的所有文本,包括任何行尾字符。请记住,在 Windows 下,将有两个这样的字符 ('\r\n');所有行号都从零开始。以下是一个简单的函数,用于删除行尾字符

  -- removes end-of-line characters in a string
  function Chomp(line)
    return string.gsub(line, "[\r\n]+$", "")
  end

editor:GetSelText() 将检索当前选定的文本。

文档的字符长度为 editor.Length,行数为 editor.LineCount;请注意这里使用的不同语法,因为 LengthLineCount属性。另一个例子是 editor.CharAt[p],它将获取位置 p 处的字符。这将是字符代码,因此请使用 string.char(ch) 生成字符串

  -- returns the character at position p as a string
  function char_at(p)
     return string.char(editor.CharAt[p])
  end

editor:textrange(p1,p2) 将获取 p1p2 之间的文本;(这是一个 SciTE 面板函数)。因此,获取位置 p 处的字符作为字符串的另一种方法是 editor:textrange(p,p+1)

editor:ClearAll() 将清除文档。

editor:AppendText(s) 将在文档末尾追加 s,而 editor:InsertText(p,s) 将在位置 p 插入 s;位置 -1 表示当前位置。这是光标当前显示的位置;在所有情况下,请注意 InsertText 不会将文本滚动到视图中。editor:ScrollCaret() 将为您执行此操作。

editor:ReplaceSel(s) 将用 s 替换选定内容。以下是一个将选定文本用粗体标签括起来的函数

  function make_bold()
    local txt = editor:GetSelText();
    editor:ReplaceSel('<b>'..txt..'</b>')
  end

选择和位置信息

要移动到新位置,请使用 editor:GotoPos(p)editor:GotoLine(l)。它们将始终使光标可见。

给定位置 peditor:LineFromPosition(p) 将为您提供行号,而 editor:PositionFromLine(l) 将为您提供行开头的位置。如果您需要行尾的位置,请使用 editor.LineEndPosition[l](请注意,属性的访问方式就像数组一样)。

editor.CurrentPos 将返回当前光标位置;这是一个可写属性,因此 editor.CurrentPos = p 也有效,但它与 editor:GotoPos(p) 的含义不同。Scintilla 中的选择位于 _锚点_ 和 _位置_ 之间,因此如果存在现有选择,则直接设置位置将更改选择。editor:SetSel(p1,p2) 是明确设置选择的最佳方法。

要找出当前可见的文档部分,请使用 editor.FirstVisibleLine 找出起始行号,并使用 editor.LinesOnScreen 找出页面上可见的行数。

center_pos() 是一个有用的函数,它使用此信息将显示内容居中在某个位置。

 -- this centers the cursor position
 function center_pos(line)
  if not line then 
     -- this is the current line
     line = editor:LineFromPosition(editor.CurrentPos)
  end
  local top = editor.FirstVisibleLine
  local middle = top + editor.LinesOnScreen/2
  editor:LineScroll(0,line - middle)
 end

SciTE 属性

有一个名为 props 的伪数组,它可以访问任何已定义的 SciTE 属性。例如,props['FilePath'] 将为您提供当前正在编辑的文件的完整路径。以下是一个非常简单的函数,它将 C++ 文件与其头文件交换,假设扩展名仅为 .cpp 和 .h

  function swap_header()
    local cpp_ext = 'cpp'
    local h_ext = 'h'
    local f = props['FileName']    -- e.g 'test'
    local ext = props['FileExt']   -- e.g 'cpp'
    local path = props['FileDir']  -- e.g. '/home/steve/progs'
    if ext == cpp_ext then
       ext = h_ext
    elseif ext == h_ext then
       ext = cpp_ext
    end
    scite.Open(path..'/'..f..'.'..ext)
  end

请参阅 SciTE 文档中名为“属性文件”的部分,以获取环境设置的所有属性列表。

请记住,由 View|Parameters 定义的参数可作为 prop[1]、prop[2]、prop[3] 和 prop[4] 访问。

当然,您可以访问任何已定义的属性,例如 props['position.height'] 将给出 SciTE 窗口的初始高度。可以定义一些专门用于脚本读取的特殊属性。为了使 swap_header() 更通用,请定义一个名为 'cpp.swap.ext' 的属性作为您选择的 C++ 源代码扩展名,并将 cpp_ext 设置为此属性。

  local cpp_ext = props['cpp.swap.ext']
  ...
然后在您的本地属性文件中定义 'cpp.swap.ext=cxx'(或任何其他值)。

脚本可以更改属性,但这只是暂时的。这里有一些让脚本开发人员更轻松的东西;通常,每个要调用的 Lua 函数都需要在属性文件中指定,但这些属性可以自动生成,没有理由这样做。以下是 SciteExtManscite_Command 的重要部分。

     -- we are fed something like 'Process File|ProcessFile|Ctrl+M'
     local name,cmd,shortcut = split3(v,'|')
     local which = '.'..idx..'.*'
     props['command.name'..which] = name
     props['command'..which] = cmd     
     props['command.subsystem'..which] = '3'
     props['command.mode'..which] = 'savebefore:no'
     props['command.shortcut'..which] = shortcut

搜索和替换

要查找当前文档中的文本,请使用editor:findtext()。它返回两个表示返回范围的位置,如果找不到匹配项则返回 nil。此函数会打印出所有包含给定文本的行。

  function all_lines_with_text(txt,flags)
    if not flags then flags = 0 end
    local s,e = editor:findtext(txt,flags,0)
    while s do 
      local l = editor:LineFromPosition(s)
      trace(l..' '..editor:GetLine(l))
      s,e = editor:findtext(txt,flags,e+1)
    end    
  end

(这里我使用trace()而不是print(),因为该行已经包含换行符)

搜索标志是 SCFIND_MATCHCASE、SCFIND_WHOLEWORD、SCFIND_WORDSTART 和 SCFIND_REGEXP 的组合。默认情况下,搜索是区分大小写的普通搜索。all_lines_with_text('for',SCFIND_WHOLEWORD) 将显示 C 文件中的所有 for 语句,all_lines_with_text('^#',SCFIND_REGEXP) 将显示所有预处理器语句(即行首出现的 '#')。请注意,SciTE 正则表达式与 Lua 的正则表达式不同 - 有关详细信息,请参阅 http://scintilla.sourceforge.net/ScintillaDoc.html 中的“搜索”。

执行搜索和替换的最简单方法是使用editor:match(),它会提供一个迭代器。

  function replace_all(target,repl)
    editor:BeginUndoAction()
    for m in editor:match(target) do
      m:replace(repl)
    end
    editor:EndUndoAction()
  end

使用 BeginUndoAction() 是确保可以一次撤消多个更改的通用方法。

标记

SciTE 使用标记来实现书签和标记包含错误的行等功能。共有 32 个可能的标记,Scintilla 将标记 0 到 24 用于一般用途;SciTE 使用 0 表示错误行,使用 1 表示书签。例如,editor:MarkerAdd(line,1) 将在line处添加一个书签,SciTE 将像处理其他书签一样处理它,因为它使用 Scintilla 内部列表查找书签。请记住,与往常一样,Scintilla 从零开始计数行。

以下是一个用于定义自定义标记的有用函数。

  local colours = {red = "#FF0000", blue = '#0000FF', green = '#00FF00',pink ="#FFAAAA" ,
					black = '#000000', lightblue = '#AAAAFF',lightgreen = '#AAFFAA'}

  function colour_parse(str)
    if sub(str,1,1) ~= '#' then
      str = colours[str]
    end
    return tonumber(sub(str,6,7)..sub(str,4,5)..sub(str,2,4),16)
  end

  function marker_define(idx,typ,fore,back)
    editor:MarkerDefine(idx,typ)
    if fore then editor:MarkerSetFore(idx,colour_parse(fore)) end
    if back then editor:MarkerSetBack(idx,colour_parse(back)) end
  end

用户列表

这些是下拉列表,允许用户从多个项目中选择,SciTE 使用它们来实现“完成符号”等功能。它们并不难使用;您提供一个包含指定分隔符的字符串,当用户选择一个项目时,OnUserListSelection 事件会被触发。

 function UserListShow(list)
   local s = ''
   local sep = ';'
   local n = table.getn(list)
   for i = 1,n-1 do
      s = s..list[i]..sep
   end
   s = s..list[n]
   editor.AutoCSeparator = string.byte(sep)
   editor:UserListShow(12,s)
   editor.AutoCSeparator = string.byte(' ')
 end

这里棘手的地方在于属性AutoCSeparator传递的是一个字符代码,而不是字符串。'12'只是一个数字,SciTE 内部没有使用它。

这是一个事件处理程序,它假设这些字符串代表某个目录中的 Lua 脚本。这里的想法是向用户展示一个很少使用的“一次性”脚本列表,否则这些脚本会使工具菜单混乱。

 function OnUserListSelection(tp,script)
   if tp == 12 then 
      dofile(path..'/'..script..'.lua')
   end
 end

构建文件列表需要一些工作。这里有一个非 Windows 的解决方案来解决这个问题

 function GetFiles(mask)
   local files = {}
   local tmpfile = '/tmp/stmp.txt'
   os.execute('ls -1 '..mask..' > '..tmpfile)
   local f = io.open(tmpfile)
   if not f then return files end  
   local k = 1
   for line in f:lines() do
      files[k] = line
      k = k + 1
   end
   f:close()
   return files
 end

在 Unix 和 Windows 上都能运行的代码有点棘手。有关更完整的解决方案,请参阅 SciteExtMan 中的scite_Files()

指示器

指示器目前在 SciTE 中没有使用,但脚本可以轻松地添加它们。它们可以被拼写检查工具用来用红线突出显示拼写错误的单词,或者用绿色波浪线突出显示有问题的语法。有三个可用的指示器,使用通常的 SciTE 设置。以下是一个函数,它将使用给定的指示器(0、1 或 2)来突出显示从pos开始的len个字符。

 function underline_text(pos,len,ind)
   local es = editor.EndStyled
   editor:StartStyling(pos,INDICS_MASK)
   editor:SetStyling(len,INDIC0_MASK + ind)
   editor:SetStyling(2,31)
 end

要删除下划线,请使用underline_text(pos,len,-1)。最后一个SetStyling()调用是必要的,以恢复词法分析器状态;31 是用于样式的低 5 位的掩码。如果需要,可以更改默认值。

默认指示器是

   0 green squiggly line
   1 light blue line of small T shapes
   2 light red line

可用的指示器样式是

  INDIC_PLAIN   Underlined with a single, straight line.
  INDIC_SQUIGGLE  	A squiggly underline.
  INDIC_TT  A line of small T shapes.
  INDIC_DIAGONAL  	Diagonal hatching.
  INDIC_STRIKE  	Strike out.
  INDIC_HIDDEN	An indicator with no visual effect.
  INDIC_BOX 	A rectangle around the text.

1.70 版本及之前的指示器

您可以使用以下标志
   0 resets the style
   INDIC0_MASK green word processing-like line
   INDIC1_MASK bizarre blue line
   INDIC2_MASK blue round background box
您可以像这样使用它
editor:StartStyling(editor.SelectionStart,INDICS_MASK)
editor:SetStyling(string.len(editor:GetSelText()),flag)

最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2017 年 5 月 21 日下午 3:20 GMT (差异)