Elixir語言中 Code.format_string!
相關用法介紹如下。
用法:
format_string!(string, opts \\ [])
(從 1.6.0 開始)
@spec format_string!(
binary(),
keyword()
) :: iodata()
格式化給定的代碼 string
。
格式化程序接收表示 Elixir 代碼的字符串,並根據預定義的規則返回表示格式化代碼的 iodata。
選項
-
:file
- 包含字符串的文件,用於錯誤報告 -
:line
- 字符串開始的行,用於錯誤報告 -
:line_length
- 格式化文檔時的目標行長。默認為 98。請注意,此值用作指導,但在某些情況下不強製執行。有關詳細信息,請參閱下麵的 "Line length" 部分 -
:locals_without_parens
- 名稱和數量對的關鍵字列表,應盡可能保持不帶括號。元數可能是原子:*
,這意味著該名稱的所有元數。格式化程序已經包含一個函數列表,這個選項擴充了這個列表。 -
:force_do_end_blocks
(從 v1.9.0 開始) - 當true
時,將do: ...
、else: ...
和朋友的所有內聯用法轉換為do
-end
塊。默認為false
。請注意,此選項是收斂的:一旦將其設置為true
,所有關鍵字都將被轉換。如果稍後將其設置為false
,則do
-end
塊將不會轉換回關鍵字。
設計原則
格式化程序是根據三個原則設計的。
首先,格式化程序默認不會改變代碼的語義。這意味著輸入 AST 和輸出 AST 是等價的。
第二個原則是提供盡可能少的配置。這通過消除爭用點來簡化格式化程序的采用,同時確保整個社區始終遵循單一樣式。
格式化程序不會硬編碼名稱。格式化程序不會有特殊行為,因為函數名為 defmodule
、 def
等。這一原則反映了 Elixir 成為可擴展語言的目標,開發人員可以使用新結構擴展語言,就好像它們是語言的一部分一樣。當絕對需要根據名稱更改行為時,此行為應該是可配置的,例如 :locals_without_parens
選項。
運行格式化程序
格式化程序嘗試在單行上盡可能地適應,並在不能時盡可能引入換行符。
在某些情況下,這可能會導致不希望的格式。因此,格式化程序生成的某些代碼可能不美觀,可能需要開發人員的明確幹預。這就是我們不建議在現有代碼庫中盲目運行格式化程序的原因。相反,您應該對每個格式化文件進行格式化和完整性檢查。
例如,格式化程序可能會破壞多個子句的長函數定義:
def my_function(
%User{name: name, age: age, ...},
arg1,
arg2
) do
...
end
雖然上麵的代碼完全有效,但您可能更喜歡匹配函數體內的結構變量,以便將定義保持在一行:
def my_function(%User{} = user, arg1, arg2) do
%{name: name, age: age, ...} = user
...
end
在某些情況下,您可以使用格式化程序不會生成優雅代碼的事實作為重構的提示。拿這個代碼:
def board?(board_id, %User{} = user, available_permissions, required_permissions) do
Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
required_permissions == Enum.to_list(MapSet.intersection(MapSet.new(required_permissions), MapSet.new(available_permissions)))
end
上麵的代碼行很長,運行格式化程序並不能解決這個問題。事實上,格式化程序可能會讓你有複雜的表達式更加明顯:
def board?(board_id, %User{} = user, available_permissions, required_permissions) do
Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
required_permissions ==
Enum.to_list(
MapSet.intersection(
MapSet.new(required_permissions),
MapSet.new(available_permissions)
)
)
end
將這種情況作為您的代碼應該重構的建議:
def board?(board_id, %User{} = user, available_permissions, required_permissions) do
Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
matching_permissions?(required_permissions, available_permissions)
end
defp matching_permissions?(required_permissions, available_permissions) do
intersection =
required_permissions
|> MapSet.new()
|> MapSet.intersection(MapSet.new(available_permissions))
|> Enum.to_list()
required_permissions == intersection
end
總結一下:由於格式化程序無法更改代碼的語義,因此有時需要調整或重構代碼以獲得最佳格式。為了幫助更好地理解如何控製格式化程序,我們將在接下來的部分中說明格式化程序保持用戶編碼的情況以及如何控製多行表達式。
線長
關於格式化程序的另一點是 :line_length
配置是一個指南。在許多情況下,格式化程序不可能將您的代碼分開,這意味著它將超過行長。例如,如果您有一個長字符串:
"this is a very long string that will go over the line length"
格式化程序不知道如何在不更改代碼底層語法表示的情況下將其分解,因此您可以介入:
"this is a very long string " <>
"that will go over the line length"
字符串連接使代碼適合單行,也為格式化程序提供了更多選項。
這也可能出現在 do/end 塊中,其中 do
關鍵字(或 ->
)可能超過行長,因為格式化程序沒有機會以可讀的方式引入換行符。例如,如果您這樣做:
case very_long_expression() do
end
隻有 do
關鍵字在行長之上,Elixir 不會發出這個:
case very_long_expression()
do
end
因此,它寧願根本不接觸線,並將do
留在線限製之上。
保持用戶的格式
在某些情況下,格式化程序尊重輸入格式。下麵列出了這些:
-
數字中的無關緊要的數字保持原樣。然而,格式化程序總是為超過 5 位的十進製數字插入下劃線,並將十六進製數字轉換為大寫
-
字符串、charlists、atoms 和 sigils 保持原樣。沒有字符會自動轉義或未轉義。分隔符的選擇也從輸入中得到尊重
-
塊內的換行符與輸入一樣保留,除了:
- 包含多行的表達式在前後總是有一個空行,並且 2) 空行總是被擠在一起形成一個空行
-
:do
關鍵字和do
-end
塊之間的選擇留給用戶 -
如果列表、元組、位串、映射、結構和函數調用後跟在左括號中的換行符和在右括號中的換行符之前,它們將被分成多行
-
某些運算符(如管道運算符)和其他運算符(如比較運算符)之前的換行符
不保證上述行為。我們將來可能會刪除或添加新規則。記錄它們的目的是更好地理解格式化程序的期望。
多行列表、映射、元組等
您可以強製列表、元組、位串、映射、結構和函數調用每行有一個條目,方法是在左括號之後添加一個換行符,在右括號行之前添加一個新行。例如:
[
foo,
bar
]
如果括號周圍沒有換行符,則格式化程序將嘗試將所有內容放在一行中,例如下麵的代碼片段
[foo,
bar]
將被格式化為
[foo, bar]
您還可以通過將每個條目放在自己的行上來強製在多行上呈現函數調用和關鍵字:
defstruct name: nil,
age: 0
上麵的代碼將由格式化程序每行保留一個關鍵字條目。為避免這種情況,隻需將所有內容壓縮成一行。
函數調用中的括號和無括號
Elixir 有兩種函數調用語法。有括號和沒有括號。默認情況下,Elixir 將為所有調用添加括號,除了:
- 具有
do
-end
塊的調用 - 沒有括號的本地調用,其中本地調用的名稱和數量也列在
:locals_without_parens
下(除了數量為 0 的調用,編譯器總是需要括號)
選擇括號和不選擇括號也會影響縮進。當帶括號的函數調用不適合同一行時,格式化程序會在括號周圍引入換行符,並用兩個空格縮進參數:
some_call(
arg1,
arg2,
arg3
)
另一方麵,沒有括號的函數調用總是由函數調用長度本身縮進,如下所示:
some_call arg1,
arg2,
arg3
如果最後一個參數是一個數據結構,例如映射和列表,並且數據結構的開頭與函數調用位於同一行,則不會發生縮進,這允許這樣的代碼:
Enum.reduce(some_collection, initial_value, fn element, acc ->
# code
end)
some_function_without_parens %{
foo: :bar,
baz: :bat
}
代碼注釋
格式化程序還處理代碼注釋以保證在注釋的開頭 (#) 和下一個字符之間始終添加一個空格。
格式化程序還將所有尾隨注釋提取到其上一行。例如下麵的代碼
hello #world
將被改寫為
# world
hello
因為代碼注釋是在代碼表示 (AST) 之外處理的,所以在某些情況下代碼注釋被代碼格式化程序視為模棱兩可。例如下麵匿名函數中的注釋
fn
arg1 ->
body1
# comment
arg2 ->
body2
end
在這個
fn
arg1 ->
body1
# comment
arg2 ->
body2
end
被認為是等效的(嵌套與大多數用戶格式一起被丟棄)。在這種情況下,代碼格式化程序將始終格式化為後者。
換行符
格式化程序將代碼中的所有換行符從 \r\n
轉換為 \n
。
相關用法
- Elixir Code.fetch_docs用法及代碼示例
- Elixir Code.prepend_path用法及代碼示例
- Elixir Code.compiler_options用法及代碼示例
- Elixir Code.quoted_to_algebra用法及代碼示例
- Elixir Code.put_compiler_option用法及代碼示例
- Elixir Code.ensure_compiled用法及代碼示例
- Elixir Code.required_files用法及代碼示例
- Elixir Code.get_compiler_option用法及代碼示例
- Elixir Code.Fragment.cursor_context用法及代碼示例
- Elixir Code.available_compiler_options用法及代碼示例
- Elixir Code.ensure_loaded?用法及代碼示例
- Elixir Code.eval_quoted用法及代碼示例
- Elixir Code.require_file用法及代碼示例
- Elixir Code.Fragment.container_cursor_to_quoted用法及代碼示例
- Elixir Code.Fragment.surround_context用法及代碼示例
- Elixir Code.delete_path用法及代碼示例
- Elixir Code.append_path用法及代碼示例
- Elixir Code.ensure_loaded用法及代碼示例
- Elixir Code.unrequire_files用法及代碼示例
- Elixir Code.string_to_quoted_with_comments用法及代碼示例
- Elixir Code.eval_string用法及代碼示例
- Elixir Code用法及代碼示例
- Elixir Config.config_env用法及代碼示例
- Elixir Config.config用法及代碼示例
- Elixir Config.Reader用法及代碼示例
注:本文由純淨天空篩選整理自elixir-lang.org大神的英文原創作品 Code.format_string!(string, opts \\ [])。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。