使用 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
将此代码放入您的主目录中的 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
技巧进行实验。
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
;请注意这里使用的不同语法,因为 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 属性。例如,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 函数都需要在属性文件中指定,但这些属性可以自动生成,没有理由这样做。以下是 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 中的“搜索”。
执行搜索和替换的最简单方法是使用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.
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)