SciTE巨集擴充器

lua-users home
wiki

SciTE 巨集擴充器

我一向喜歡 C 預處理器的巨集功能。對我來說,這個巨集功能能夠讓 C 程式碼更具可讀性,且減少錯誤的可能性

 #define FOR(i,n) for(i = 0; i < (n); i++)

不幸的是,受濫用的影響導致過去規則使用陳述式巨集的方式已不受歡迎,而 C++ 的程式設計風格一般都反對使用它們(Stroustrup 一如往常地採取更務實的路線)

SciTE Lua 巨集擴充功能是一個簡潔的巨集預處理器,它與縮寫符號類似,可以擴充當前緩衝區中的巨集。例如,我可以在我的屬性檔中放入

 macro.subst.1=for(i,n)=for(i = 0; i < n; i++)

然後在緩衝區中輸入 FOR(k,10),接著按「擴充巨集」(Alt+Enter)。一個簡單的單次遍歷擴充會開始運行,接著 for(k = 0; k < 10; k++) 會取代緩衝區中的巨集。

由於這是一個不會自動執行的單次遍歷擴充,因此在這裡使用「for」等關鍵字時沒有問題。

行饋線會在巨集定義中使用「\n」表示,你隨時可以使用反斜線將定義拆分為多行。

 macro.subst.2=if(cond)=if(cond) { \n } else { \n }

與縮寫符號不同的是,擴充出來的程式碼不會自動縮排。將縮寫符號使用的自動格式化程式碼公諸於世並非難事,但在那之前,我們的構想是使用標準版本。

這個巨集擴充器最有趣的地方在於它可以使用 Lua 輕鬆擴充。巨集中如果出現從 $ 開頭的任何函式呼叫,皆視為呼叫全域定義的 Lua 函式。偽函式「eval」永遠可用,可用於讓你將 Lua 表達式直接放入巨集中

 macro.subst.3=date=$eval(os.date())

這個巨集沒有引數,且會在擴充時將日期的合理表示法插入到你的目前檔案中。

我定義了 $cat 來執行「符號串接」,且定義了 $quote 來執行「字串化」,而不使用帶有 # 和 ## 的雜亂寫法。於是

    newfn(name)=void $cat(INIT_,name) (int i) {\n}\n

然後 newfn'first' 會擴充為

 void INIT_first(int i) {
 }

請注意,你可以使用引號(單引號或雙引號)傳遞一個引數給巨集,而不使用傳統的括號。

你不必指定巨集的所有引數;如果沒有使用引數,則視為 nil。將引數傳遞給 Lua 函式時可以很方便地使用這個方式。

設定

Files:wiki_insecure/editors/SciTE/macro.lua 的內容放入你現有的 Lua 啟動檔中,或以

 ext.lua.startup.script=$(SciteDefaultHome)/macro.lua

這個需要絕對路徑——SciTE 屬性在這裡非常好用!

在你的屬性檔中,將兩個按鍵與函式 do_macro 和 macro_select 繫結(改成你自己覺得最好的方式,但請記住你不能覆寫 SciTE 定義的快速鍵)。這些 command.mode 行有必要避免那些惱人的「你想要儲存嗎」訊息。

 command.name.11.*=Expand Macro
 command.11.*=do_macro
 command.subsystem.11.*=3
 command.mode.11.*=savebefore:no
 command.shortcut.11.*=Alt+Enter

 command.name.12.*=Macro Select
 command.12.*=macro_select
 command.subsystem.12.*=3
 command.mode.12.*=savebefore:no
 command.shortcut.12.*=Ctrl+=

現在您可以定義一些巨集(它們可以放置在任何已載入的屬性檔案中)

 macro.subst.1=for(i,n)=for(i = 0; i < n; i++)
 macro.subst.2=if(cond)=if(cond) { \n \
                                  \n \
  } else {                        \n \
                                  \n \
  } 
 macro.subst.3=test(xx)=$quote(xx)  
 macro.subst.4=date=$eval(os.date())
 macro.subst.5=class(name,base)=$do_class(name,base)
 macro.subst.6=insert(x)=$insert_file(x)
 macro.subst.7=i(x)=$international_string(x)
 macro.subst.8=_(x)=$upcase(x)

範例

撰寫 C++ 類別本體

在此我們利用了以下事實:如果未指定巨集引數,則把它視為 nil。

 class(name,base)=$do_class(name,base)

其中

 function do_class(name,base)
 local res = 'class '..name
 if base then 
    res = res..': public '..base
 end
 return res..' {\n public:\n};\n'
 end

現在展開 class(Dog,Animal) 將產生

 class Dog: public Animal {
 public:
 };

而展開 class(Device) 將產生

 class Device {
 public:
 };

包含文字檔案

偶爾會很有用,但只會偶爾使用,所以不值得定義一般指令。此外,通常在 Lua 中定義的指令無法提示使用者輸入。(或許這是一種有用的能力,但這是現有 1.6.1 版本可以做到的)

要使用,請輸入 insert"file",然後使用 Alt+Enter 來展開巨集。

 ... insert(x)=$insert_file(x)

 function insert_file(file)
  local f = io.open(file)
  local txt = f:read('*a')
  if txt then
    f:close()
    return txt
  else
    return ""
  end
 end

國際化

我們希望撰寫一個可國際化的應用程式,但希望避免所有繁瑣的定義。

這是一個巨集範例,除了實際插入程式中的文字外,有一些效用會更新標頭檔。

以下是顯示如何解決這項問題的巨集

 i(x)=$international_string(x)

其中 Lua 函數為

 function international_string(x)
  local id = convert_to_iden(x)
  local f = io.open('international.h','a')
  f:write('#define '..id..' '..quote(x)..'\n')
  f:close()
  return id
 end

我們需要將字串轉換為 C 式樣識別碼,方法是將所有非法字元換成底線,並將結果修剪成某個最大長度。

 function convert_to_iden(x)
  local s = string.upper(x)
  s = string.gsub(s,'[^%w_]','_')
  return string.sub(s,1,32)
 end

要使用此巨集,請在開頭輸入字串 'i',然後按 Alt+Enter 來呼叫巨集:MessageBox(i"Please insert a disk" 會變成 MessageBox(PLEASE_INSERT_A_DISK,而 'international.h' 將自動更新!您當然可以用 props['InternationalHeader?'] 自訂這個名稱,並將此名稱定義為屬性。

將選取的文字轉換為大寫

任何名為 '_' 的巨集都有一個特殊意義;它會由目前選取文字的「對選取進行處理」來呼叫。

 _(x)=$upcase(x)

Lua 中為

  function upcase(x) return string.upper(x) end

現在,Ctrl+= 將會轉換選取的文字。

「對選取進行處理」機制似乎僵化,因為一次只能使用一個處理。以下是如何動態重新定義這個巨集的技巧。新增此巨集

 def(op)=$add_macro(x)

然後您可以在任何地方展開 def 來新增一個巨集。例如,展開 def'_(x)=$quote(x) 會將選取處理變更為引號處理。

自動建立 C/C++ 標頭檔

這是某事我做了好幾百次,但小小自動化可以讓這個繁瑣的任務變得簡單。

 include(file)=$create_include(file)

 function create_include(file)
  local id = '__'..convert_to_iden(file)
  local path 
  local find = string.find
  -- is it absolute? If not, then prepend the current file's path
  if find(file,'^[/\\]') or find(file,'^%a:') then
     path = file
  else
     path = props['FileDir']..'/'..f
  end
  local f = io.open(path,'w')
  local copyright = props['CopyrightNotice']
  if copyright then f:write(copyright) end
  f:write '\n'
  f:write ('#ifndef '..id..'\n')
  f:write ('#define '..id..'\n\n')
  f:write ('#endif\n')
  f:close()
  
  return '#include '..quote(file)
 end

其中 convert_to_iden 之前已定義,而 quote 已由 Lua 巨集套件定義。一個明顯的改進是檢查檔案是否已經存在,並提供建立檔案位置更大的彈性。在插入 #include 之後,您可以使用開啟選取的檔名(Ctrl+Shift+O)來開啟檔案。

可能的改善

使用 OnKey,擴充 Enter 並不會很難 (請見 Sebastian 的 SciteLatex,這是個範例)。甚至可以自動擴充巨集,方法是觀看輸入的字詞 (例如,請見 SciteWordSubstitution),但這可能會令人有點憂心。(當然,如果您得到無法預期的擴充,Ctrl+Z 始終是您的好朋友。)如果插入的文字能遵守目前語言的縮寫等縮寫規則,將會很酷,但那需要 SciTE 本身進行一些變更,而且可能並非總是需要 (所以問題是:如何表示需要智慧插入?)

永遠記住所有已定義的巨集並不容易,因此下拉式清單會很有用。要做到這一點並不難,方法是使用 editor:UserListShowOnUserListSelection;如果巨集沒有帶參數,則選取該巨集就會立即展開它。

對我來說,最令人興奮且開放式功能是能夠「內嵌」執行任意 Lua 程式碼,其限制僅受限於我們的創意。

SteveDonovan


最近變更 · 喜好設定
編輯 · 歷史
最後編輯於 2007 年 3 月 7 日 上午 2:43 GMT (差異)