將 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,而 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 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;請注意這裡使用不同的語法,因為 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'] 會提供目前正在編輯的檔案之完整路徑。以下是一個非常簡單的函式,假設副檔名只有 .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 函式都必須在屬性檔案中指定,但這些屬性沒有理由不能自動產生。以下是 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

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.

版本 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 (diff)