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)
在此我們利用了以下事實:如果未指定巨集引數,則把它視為 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)
會將選取處理變更為引號處理。
這是某事我做了好幾百次,但小小自動化可以讓這個繁瑣的任務變得簡單。
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:UserListShow
和 OnUserListSelection
;如果巨集沒有帶參數,則選取該巨集就會立即展開它。
對我來說,最令人興奮且開放式功能是能夠「內嵌」執行任意 Lua 程式碼,其限制僅受限於我們的創意。