將 Lua 與 Scite 搭配使用 |
|
以下是一個簡單範例;將此程式碼放入您的屬性檔案中(可以是本機、使用者或全域)
command.name.1.*=Load Lua command.subsystem.1.*=3 command.1.*=dofile $(FilePath)
您現在會在「工具」功能表中看到一個名為「載入 Lua」的功能表項目,而預設快速鍵將會是 Ctrl+1。command.subsystem
必須設定為 3,而 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 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');所有行號都是從 0 開始的。以下是移除行尾字元的簡單函數
-- 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']
會提供目前正在編輯的檔案之完整路徑。以下是一個非常簡單的函式,假設副檔名只有 .cpp 和 .h,則會將 C++ 檔案與其標頭互換
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 文件中的「屬性檔案」一節,取得環境設定的屬性之完整清單。
請記住,由「檢視 | 參數」定義的參數可透過 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
Using BeginUndoAction()
是確保可以一次復原多項變更的一般方法。
SciTE 使用標記來實作書籤和標示錯誤行等功能。一共有 32 個可能的標記,而 Scintilla 將標記 0 到 24 供一般使用;SciTE 會使用 0 來標示錯誤行,並使用 1 來標示書籤。例如,editor:MarkerAdd(line,1)
會在 line
放置一個書籤,而 SciTE 會將它當作任何其他書籤來處理,因為它會使用 Scintilla 內部清單來尋找書籤。請務必記住,SciTE 一貫會將行數從 0 開始算起。
以下是一個有助於定義自訂標記的函式
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)