1.87.2
, ОС — Ubunu 23.10.v0.41.2
go1.21.0 linux/amd64
Я пишу этот фрагмент кода в своих файлах *.go
Golang:
switch {
case myString == "":
fmt.Println("Empty string")
case myString == "foo":
doFoo()
case myString == "bar":
fmt.Println("We got bar now!")
default:
fmt.Println("Another default")
}
И код VS форматирует его как:
switch {
case myString == "":
fmt.Println("Empty string")
case myString == "foo":
doFoo()
case myString == "bar":
fmt.Println("We got bar now!")
default:
fmt.Println("Another default")
}
Вопрос, есть ли способ настроить VSCode/расширение Go/другое РАЗУМНО (т. е. без необходимости писать собственное расширение VS Code или взламывать необычные странные настройки), чтобы можно было сохранить форматирование фрагмента 1?
Что я пробовал:
gofumpt
, gofmt
, goimports
Не уверен, что не так.
Я никогда не понимал, почему мы делаем отступы внутри блока, а не case
внутри switch
. Это очень распространено во многих языках.
@Brits Я был бы рад, если бы вы прочитали вопрос, прежде чем комментировать. Я уточнил, что тоже пробовал gofmt. Спасибо.
@JJF Я это понимаю. Предупреждаю вас, что формат, реализованный в gofmt
(часть стандартного инструментария), является стандартным форматом кода Go; как говорится в блоге, на который я ссылаюсь, «бесспорно: никогда больше не спорьте о интервалах или положении скобок!». Вам будет сложно найти инструменты, реализующие разные стандарты (запрашивать их будет не по теме), и я настоятельно рекомендую вам принять стандарт (есть некоторые вещи, которые вам не нравятся, но преимущества использования одного и того же формата как и все остальные это перевешивают).
@Brits, спасибо за разъяснения. Вы имеете в виду, что первый фрагмент — это формат, реализованный gofmt, то есть стандарт? И поэтому форматирование, которое хотелось бы иметь, нестандартное?
@JJF Второе «Форматирование кода VS» — это стандарт Go. Например, он используется в Эффективном Го.
Visual Studio Code не знает Go. В процессе настройки вы установили стандартный плагин Go, и этот стандартный плагин Go установил стандартный языковой сервер Go, который выполняет все форматирование. На самом деле рекомендуется просто не кататься на коньках в гору и следовать установленным стандартам. Даже если он делает непростительные вещи вроде отступов при табуляции.
@Schwern: именно этот фрагмент gofmt
мне тоже показался наиболее нелогичным, но я вижу некоторую логику, если вы приравняете switch + case
к набору if-else if
условных операторов. Все else if
будут на одном уровне, блок с отступом соответствует утверждениям в блоке else if
. Таким образом, switch
вроде означает if exp := <switch expression>; if exp == <case1> {} else if exp == <case2> {}
, тогда отступ имеет смысл
@EliasVanOotegem Эй, спасибо за попытку объяснить, а не просто сказать людям следовать стандарту. Стандарты должны что-то означать. Однако по этой логике код в циклах for не будет иметь отступов. Вы только что описали вложенный if. Отступы предназначены для быстрого определения того, какой код находится внутри блока, стиль Go нарушает это. Все это возврат к древним стилям C, когда это было ближе к таблице переходов и метки «case» не имели отступов. Некоторые люди также не делают отступов в SQL.
@schwern: код в циклах будет с отступом, как и код блока case
. Условия/оценки else if
не имеют отступа относительно окружающего блока или исходного предложения if
. Это не имеет ничего общего с вложенными. Конечно, это обычная вещь в некоторых стилях C, и когда вы спросите какого-нибудь старшего коллегу-разработчика C о причине, почему этот стиль имеет для них смысл, вы получите ответ, который я дал (говорю по опыту).
@EliasVanOotegem И регистры зависят от переключателя, например, switch a { ... }
, они также должны быть с отступом, чтобы показать, что они являются частью блока переключателей. Я уверен, что вы получили много объяснений. Еще я понял, что переключатель не является настоящим блоком. Верно, но не показательно. Стиль должен облегчить понимание кода с первого взгляда: эти случаи являются частью этого переключателя. Если это требует сложного обоснования, это никому не поможет, за исключением, может быть, людей, которые поступали таким образом на протяжении десятилетий.
@Schwern: Я согласен, что стиль призван сделать код более понятным. Речь идет просто о том, что разные люди интерпретируют/концептуально отображают оператор переключения (и его случаи) по-разному. AST go явно придерживается мнения, что оператор переключения содержит часть предложений case (т. е. предложения case составляют предложение переключения). Каждое предложение случая содержит фактический блок операторов. Для справки исходный код компилятора go
Посмотрите, я раньше делал отступы во всех предложениях на языке C или на любом другом языке. Формат go поначалу выглядел немного странно, но это привычка. Это совсем не мешает читабельности, когда вы к этому привыкнете. Я посмотрел на компилятор, чтобы узнать, что могло повлиять на выбор стиля, и обнаружил, что предложение case в терминах AST состоит из узла case
(например, TypeSwitchStmt
) и узла Assign Stmt
(CaseClause
). Все условие определения того, выполнять ли List []Expr
под Block *BlockStmt
, зависит как от оператора switch, так и от оператора
Стиль @EliasVanOotegem заключается в том, чтобы предоставить людям информацию о коде с первого взгляда. Если это имеет смысл только после детального изучения реализации, значит, оно не работает.
@Шверн, да, но не то, что я сказал. Я имел в виду, что даже без объяснений и проверки АСТ я уже привык к стилю госмта. До этого я был ярым сторонником стиля Оллмана. TL;DR: в лучшем случае это придирка, несложно привыкнуть к предложениям без отступов. В проектах WRT с открытым исходным кодом лучше всего иметь единый стиль кода, чтобы каждый мог быстро анализировать код, а не собственный стиль, который требует изменения конфигураций редактора в зависимости от проекта и т. д.
Это действительно стиль Go, который по какой-то причине не делает отступов внутри switch
, возможно, это пережиток стилей C. Например, этот стиль используется в Эффективное Го. Кроме того, сообщество Go обычно против изменения стиля, а их инструменты обычно не настраиваются. Собственный языковой сервер VSCode и все упомянутые вами инструменты Go будут следовать этому стилю.
Однако goformat позволит вам настроить стиль. Например, indent=2 switch enter=1
даст вам более знакомый стиль с отступом в 2 символа и блоками переключателей.
goformat.exe -style "indent=2 switch enter=1" .\tmp\test.go
package main
import "fmt"
func main() {
myString := "foo"
switch {
case myString == "":
fmt.Println("Empty string")
case myString == "foo":
fmt.Println("foo")
case myString == "bar":
fmt.Println("We got bar now!")
default:
fmt.Println("Another default")
}
}
VSCode можно настроить для использования goformat
. В расширении Go найдите «Флаги форматирования» и «Инструмент форматирования». Вы можете поместить выбранные стили в файл и зафиксировать его в своем проекте.
Исправление: это не «более знакомый стиль» в терминах GoLang. В этом суть правительства. Использование goformat или других средств форматирования — это хорошо, если вы работаете над собственным кодом в полной изоляции, но как только вы работаете над кодом вместе с другими, либо в команде, либо внося свой вклад в проект с открытым исходным кодом, индивидуальные настройки форматирования становятся постоянное сопротивление, именно поэтому GoLang так самоуверен в этом вопросе; goformat, возможно, был создан из лучших побуждений, но, по моему мнению, это ошибочно, и его следует избегать, и, конечно, не рекомендуется.
@Deltics Я не рекомендовал, я ответил на вопрос. Они могут принять это или оставить. Это более знакомый стиль для тех, кто пришел из других языков и еще не пил кул-эйд. Какой вкус был у вас?
@Deltics Большинство языков решили проблему хаоса стилей, предоставляя файлы конфигурации стилей для каждого проекта, поэтому люди автоматически следуют стилю проекта. Есть даже универсальный вариант EditorConfig. Тогда, если язык решит, ох, я не знаю, сохранить устаревшие стили, такие как жесткие вкладки и притворяться, что переключатель/регистр - это таблица переходов, это может быть исправлено сообществом. Позвольте мне рассказать вам о 4-символьном отступе со смешанными жесткими табуляциями и пробелами (1 отступ = 4 пробела, 2 отступа = жесткий табуляция, 3 = табуляция + 4 пробела), который раньше был стандартом Perl. Да.
конфигурация каждого проекта - это не «решение», это лейкопластырь. Он не учитывает тот факт, что разные люди работают над разными проектами, поэтому потенциально переключение между проектами (в этом случае) означает необходимость приспосабливаться к разным стилям, если только вы не работаете над проектами только в среде, где применяется единый стиль. Но при этом остается проблема определения того, каким должен быть этот «последовательный стиль», и возможность его изменения (под видом «улучшенного»), что оставляет дверь открытой для продолжающихся дебатов и аргументов. Как я говорю, это не «решение».
@Deltics Я много прыгаю с языков и занимаюсь проектами. Стили по умолчанию полезны, но часто имеют недостатки и никогда не удовлетворят всех. Я предпочитаю стиль с четко сформулированными изменениями и автоматическим корректором стиля. Небольшие различия в том, что я пишу, не имеют значения, они автоматически корректируются, и я приду изучать новый стиль. Что касается «оставления двери открытой для постоянных дебатов и споров», я считаю, что проблема часто заключается в том, что люди не понимают, как работают стили, и вместо этого выступают за знакомство и последовательность как цель; это не цели, а средства достижения цели. Эти споры никогда не закончатся.
«Эти споры никогда не закончатся». точно!! Есть один сценарий, в котором эти аргументы БЫЛИ навсегда прекращены... в командах, использующих golang, которые приняли стандарт gomft и сопротивлялись искушению попытаться сделать golang похожим на какой-то другой язык, с которым они также могут быть знакомы. . :)
@Deltics И все же мы здесь об этом спорим. Подход, основанный на единственном истинном стиле, рассматривает стиль как произвольный; самое главное, чтобы все использовали один и тот же стиль. Стиль не произволен, в нем много полезного, если вы его понимаете, а OTS это лишает. Речь идет не о попытках относиться к Go как к другому языку, хотя стиль переключения Go — это C примерно 1995 года. Чтобы остановить бесконечные споры о стиле и обеспечить гибкость, проект начинается со стилем по умолчанию и кодифицирует его изменения как конфигурацию форматирования. Если ваш проект не может согласовать изменения конфигурации форматтера, как они согласовывают что-либо?
конечно, в любой команде есть разногласия и пути и средства их разрешения и достижения консенсуса. Принятие идиоматических gofmt
стандартов устраняет один источник разногласий, позволяя командам сосредоточиться на достижении согласия по более важным вопросам. Работая в команде golang, где мы никогда не обсуждали форматирование (кроме того, чтобы понять роль gofmt), вместе с командами Java, которые тратят абсурдное количество времени на споры, дебаты, ратификацию и контроль/обеспечение соблюдения стандартов «корпоративного формата», в то время как мы продолжать работу, которая важна для наших пользователей/клиентов.
VS Code не форматирует ваш код. По крайней мере, не напрямую.
Расширение GoLang отвечает за форматирование и форматирует ваш код при каждом сохранении файла. По умолчанию он использует gofmt
, вездесущий и авторитетный форматтер, принятый большинством разработчиков Go.
Да, расширение предоставляет настройку, позволяющую настроить использование альтернативного форматтера, если вы настаиваете. Но ИМХО, лучший совет — научиться работать со стандартным форматированием и ценить его, а не бороться с ним.
Как вы заметили, некоторых людей некоторые аспекты форматирования gofmt
раздражают в разной степени. Я уверен, что по мере приобретения опыта работы с GoLang вы столкнетесь с другими из них. Могу сказать только одно: раздражение пройдет.
В командах не следует недооценивать ценность времени, споров и разочарований, сэкономленных за счет (почти полного) устранения «личного стиля», которого достигает gofmt
. Неважно и то, в какой степени это является значимым фактором, облегчающим восприятие кода GoLang, написанного практически кем угодно, без необходимости предварительной адаптации к его индивидуальному стилю, наложенному поверх синтаксиса языка.
В случаях, которые кажутся вам особенно проблематичными, обычно существует некоторая степень гибкости в рамках ограничений стандартного форматирования. Например, в случае switch/case
, gofmt
сохранит (максимум) одну строку разделения пробелов между регистрами, поэтому вы можете использовать:
switch {
case myString == "":
fmt.Println("Empty string")
case myString == "foo":
doFoo()
case myString == "bar":
fmt.Println("We got bar now!")
default:
fmt.Println("Another default")
}
Вам решать, считаете ли вы это улучшением или нет. :)
Но это пример того, как gofmt
допускает некоторую гибкость.
Эта гибкость может быть полезна и в других отношениях, помимо индивидуальных предпочтений, и может стать самостоятельным «инструментом». Например, в одном switch
вы можете смешивать/сопоставлять разделение пробелов со случаями, связанными с «группировкой», если вы считаете это полезным в конкретном случае, например:
switch {
case myString == "foo":
doFoo()
case myString == "bar":
fmt.Println("We got bar now!")
case myString == "":
fmt.Println("Empty string")
default:
fmt.Println("Another default")
}
Опять же, является ли это «улучшением» во многом субъективно, но, по крайней мере, gofmt
не помешает вам в этом вопросе. :)
Существует Го-пословица «Стиль Gofmt никому не нравится, но gofmt нравится всем». Я настоятельно рекомендую вам не бороться с этим; почти весь код Go отформатирован с использованием gofmt (или совместимого, более строгого стандарта), а отсутствие опций намеренно. См. эту публикацию в блоге для обоснования и дополнительной информации.