在 SciTE 中使用 Lua |
|
这是一个简单的例子;将其放入您的 properties 文件中(可以是 local、user 或 global)
command.name.1.*=Load Lua command.subsystem.1.*=3 command.1.*=dofile $(FilePath)
现在,“工具”菜单上将有一个名为“Load Lua”的菜单项,默认情况下它将具有快捷键 Ctrl+1。command.subsystem 必须设置为 3,这是 Lua 扩展。在这种情况下,全局 Lua 函数是 dofile,它被保存在 SciTE 的 Lua 5 中,正是因为它在这种情况下非常有用;请注意,dofile 后面没有参数列表。您的函数最多可以传递一个参数,但与 SciTE properties 文件中的任何内容一样,它可以包含对其他 SciTE properties 的引用,例如 FilePath,这是当前显示文件的完整路径。(有关这些动态 properties 的完整列表,请参阅 SciTE 手册中的“Properties File”部分)
现在,如果您编辑一个包含此内容的文件
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
将其放入您的主目录中的 Lua 文件(例如 test.lua)中
function make_uppercase() local sel = editor:GetSelText() editor:ReplaceSel(string.upper(sel)) end
并在您的 properties 文件中,放入此内容
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 确实已经有了这样的操作。但是您几乎可以用 Lua 在 SciTE 中做任何事情!我建议一个学习的好方法是尝试使用 dofile 技巧。
Lua Scintilla 绑定的最佳参考是 scintilla.iface,它位于 Scintilla include 目录中。如果您查找 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;请注意这里使用的不同语法,因为 Length 和 LineCount 是属性。另一个例子是 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) 将获取 p1 和 p2 之间的文本;(这是一个 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)。它们将始终使插入符号可见。
给定一个位置 p,editor: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
有一个名为 props 的伪数组,可以访问任何已定义的 SciTE properties。例如,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 环境设置的 properties 列表,请参阅 SciTE 文档中的“Properties File”部分。
请记住,通过 View|Parameters 定义的参数可以作为 prop[1], prop[2], prop[3] 和 prop[4] 访问。
当然,您可以访问任何已定义的 properties,例如 props['position.height'] 将给出 SciTE 窗口的初始高度。可能定义了一些专门供脚本只读的特殊 properties。为了使 swap_header() 更通用,将一个名为 'cpp.swap.ext' 的 property 定义为您的首选 C++ 源文件扩展名,并将 cpp_ext 设置为该值。
local cpp_ext = props['cpp.swap.ext'] ...然后在您的 Local properties 文件中定义 'cpp.swap.ext=cxx'(或任何您想要的)。
脚本可以修改 properties,尽管这当然只会是临时的。这里有一些使脚本开发更容易的事情;通常,每个要调用的 Lua 函数都需要在 property 文件中指定,但这些 properties 没有理由不能自动生成。以下是 SciteExtMan 中 scite_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 中的“Searching”。
进行搜索和替换的最简单方法是使用 editor:match(),它给我们一个迭代器。
function replace_all(target,repl) editor:BeginUndoAction() for m in editor:match(target) do m:replace(repl) end editor:EndUndoAction() end
Using 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 将其用于“Complete Symbol”等。它们并不难使用;您提供一个带有指定分隔符的字符串,当用户选择一个项时,会触发 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.
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)