• goland live template 牛刀小试


    goland live template 牛刀小试

    之前无意中接触到了 goland 的 live template 功能,感觉功能还挺强大的,在这里简单说明一下如何使用吧,更详细的使用方法可以去看官方文档

    live template 的作用

    在日常编码中,我们常常可能经常需要编写一些结构极其相似,可能只是变量的名字稍微有些不同的代码。在这种情况下,往往有两种解决方法:

    1. 捏着鼻子,把相似的代码再写一遍
    2. CV 大法,把之前的代码 CV 到另一个地方,然后 replace 掉有差异的地方 (例如变量名字)

    当然,你可能还会想到另一种方法,写一个工具来生成代码,这个工具以差异化的地方为参数 来生成代码,特别是对于 go,本身就提供了 template 库用于生成代码,其实也蛮方便的。但是,开发一个代码生成工具总归是有点不够轻量。

    这个时候,live template 就派上用场了,只要设置到模块以及对应的快捷键,之后每次触发快捷键时就能快速生成代码模板了,简直不要太方便。

    例子
    type T1 struct {
    	ID int64
    }
    
    type T1Slice []*T1
    
    func (slice T1Slice) IDs() []int64 {
    	ids := make([]int64, 0, len(slice))
    	for _, t1 := range slice {
    		ids = append(ids, t1.ID)
    	}
    	return ids
    }
    

    有一个名为 T1 的 struct,其内有一个类型为 int64 名为 ID 的 field

    之后新增了一个 T1Slice 的 struct,其实质上是一个 *T1 的数组,现在需要为 T1Slice 添加一个 method IDs(),它的作用是提取出每个 T1 的 ID 并以数组的形式返回。

    type T2 struct {
    	Name string
    }
    
    type T2Slice []*T2
    

    现在需要为 T2Slice 添加一个 method,该 method 的作用是取出其内每个元素的 Name field 组成数组并返回。

    很容易写出下面的代码

    func (slice T2Slice) Names() []string {
    	names := make([]string, 0, len(slice))
    	for _, t2 := range slice {
    		names = append(names, t2.Name)
    	}
    	return names
    }
    

    可以看出 T1Slice.IDs() 和 T2Slice.Names() 这两者的结构是非常相似的

    1. 他两的 receiver 都是一个名为 XXXSlice 的 struct
    2. 他两的 method name 都名为 XXXs,其中 XXX 是它们各自所返回的 field
    3. 他两返回的都是一个数组 []XXX,其中 XXX 是数组的元素类型
    4. 他两的实现都是先 make 出一个 slice,之后用一个 for range 来遍历,并往 slice 中添加元素最终返回。

    既然找到了规律,那就可以用上 live template 了。首先来看看使用 live template 的效果

    image

    该 live template 的内容

    func (slice $SLICETYPE$) $FIELDNAME$s() []$TYPE$ {
    	$fieldNAME$s := make([]$TYPE$, 0, len(slice))
    	for _, $VALUE$ := range slice {
    		$fieldNAME$s = append($fieldNAME$s, $VALUE$.$FIELDNAME$)
    	}
    	return $fieldNAME$s
    }
    

    看上去很好理解,其实 live template 就由两部分组成

    • $$包裹的变量
    • 没有用$$包裹的常量

    变量需要用户手动去输入,输完一个变量后,按回车,光标会调到下一个变量处,所有的变量输入完之后,代码也就生成完毕了。当然也可以用输入变量时用 ESC 中断,则当前的代码的状态就是最终的结果了。

    Expression

    live template 真正牛逼的地方还在于可以为变量添加 Expression,就以刚刚的例子来说,为什么我仅仅输入了 $FIELDNAME$ 最终却自动生成了 $fieldNAME$s 呢?这就是 Expression 的效果

    image

    如上,可以为 fieldNAME 添加一个 Expression ,将 FIELDNAME 的值给 camelCase 就会得到 fieldNAME。

    此外,在上面的例子中 for range T1Slice 时,自动生成的变量名为 t1,for range T2Slice 时,自动生成的变量名为 t2,这就是 goSuggestVariableName() 这个 Expression 的功能,它会根据当前的上下文生成一个变量名。

    Goland 所有的 Expression

    个人常用的 Live Template
    cd ~/Library/'Application Support'/JetBrains/GoLand2021.1/templates
    

    把下面的两个 xml 文件放在 template 目录下面即可

    <!-- Go.xml -->
    <templateSet group="Go">
      <template name="ts" value="type $NAME$Slice []*$NAME$" description="type TSlice []*T" toReformat="false" toShortenFQNames="true">
        <variable name="NAME" expression="complete()" defaultValue="&quot;Name&quot;" alwaysStopAt="true" />
        <context>
          <option name="GO_FILE" value="true" />
        </context>
      </template>
      <template name="fake" value="func fake$NAME$(opts ...func(*$NAME$)) *$NAME$ {&#10;&#9;m := &amp;$NAME${}&#10;&#9;for _, opt := range opts {&#10;&#9;&#9;opt(m)&#10;&#9;}&#10;&#9;return m&#10;}" description="fake for test" toReformat="false" toShortenFQNames="true">
        <variable name="NAME" expression="" defaultValue="&quot;Name&quot;" alwaysStopAt="true" />
        <context>
          <option name="GO_FILE" value="true" />
        </context>
      </template>
      <template name="sfms" value="func (slice $SLICETYPE$) $FIELDNAME$s() []$TYPE$ {&#10;&#9;$fieldNAME$s := make([]$TYPE$, 0, len(slice))&#10;&#9;for _, $VALUE$ := range slice {&#10;&#9;&#9;$fieldNAME$s = append($fieldNAME$s, $VALUE$.$FIELDNAME$)&#10;&#9;}&#10;&#9;return $fieldNAME$s&#10;}" description="slice field members" toReformat="false" toShortenFQNames="true">
        <variable name="SLICETYPE" expression="" defaultValue="&quot;SliceType&quot;" alwaysStopAt="true" />
        <variable name="FIELDNAME" expression="" defaultValue="&quot;FieldName&quot;" alwaysStopAt="true" />
        <variable name="TYPE" expression="" defaultValue="&quot;int&quot;" alwaysStopAt="true" />
        <variable name="fieldNAME" expression="camelCase(FIELDNAME)" defaultValue="&quot;fieldName&quot;" alwaysStopAt="true" />
        <variable name="VALUE" expression="goSuggestVariableName()" defaultValue="&quot;elem&quot;" alwaysStopAt="true" />
        <context>
          <option name="GO_FILE" value="true" />
        </context>
      </template>
      <template name="sgbf" value="func (slice $TYPE$Slice) GroupBy$FIELDNAME$() map[$KEYTYPE$]*$TYPE$ {&#10;&#9;groupBy$FIELDNAME$ := make(map[$KEYTYPE$]*$TYPE$)&#10;&#9;for _, $VALUE$ := range slice {&#10;&#9;&#9;groupBy$FIELDNAME$[$VALUE$.$FIELDNAME$]=$VALUE$&#10;&#9;}&#10;&#9;return groupBy$FIELDNAME$&#10;}" description="slice group by field" toReformat="false" toShortenFQNames="true">
        <variable name="FIELDNAME" expression="" defaultValue="" alwaysStopAt="true" />
        <variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
        <variable name="KEYTYPE" expression="" defaultValue="" alwaysStopAt="true" />
        <variable name="VALUE" expression="goSuggestVariableName()" defaultValue="&quot;elem&quot;" alwaysStopAt="true" />
        <context>
          <option name="GO" value="true" />
        </context>
      </template>
      <template name="trav" value="// TraverseTable$TYPE$ 流式遍历表。 从 id 等于 idFrom 开始取数,间隔 interval 执行一次,每次获取 limit 条数据,直到表中的数据都被遍历一遍&#10;func TraverseTable$TYPE$(engine *xorm.Engine, idFrom uint64, limit int, interval time.Duration) chan *model.$TYPE$ {&#10;&#9;result := make(chan *model.$TYPE$, limit)&#10;&#9;go func() {&#10;&#9;&#9;defer close(result)&#10;&#9;&#9;var err error&#10;&#9;&#9;for {&#10;&#9;&#9;&#9;items := make([]*model.$TYPE$, 0, limit)&#10;&#9;&#9;&#9;// TODO: 此处的 id,可以替换为其他的自增ID&#10;&#9;&#9;&#9;if err = engine.Where(&quot;id &gt;= ?&quot;, idFrom).Asc(&quot;id&quot;).Limit(limit).Find(&amp;items); err != nil {&#10;&#9;&#9;&#9;&#9;panic(err)&#10;&#9;&#9;&#9;}&#10;&#9;&#9;&#9;for _, item := range items {&#10;&#9;&#9;&#9;&#9;result &lt;- item&#10;&#9;&#9;&#9;}&#10;&#9;&#9;&#9;if len(items) == 0 {&#10;&#9;&#9;&#9;&#9;break&#10;&#9;&#9;&#9;}&#10;&#9;&#9;&#9;// TODO: 如果在 Where 中不使用 ID 则此处需要进行相应处理&#10;&#9;&#9;&#9;idFrom = items[len(items)-1].ID + 1&#10;&#9;&#9;&#9;time.Sleep(interval)&#10;&#9;&#9;}&#10;&#9;}()&#10;&#9;return result&#10;}" description="" toReformat="false" toShortenFQNames="true">
        <variable name="TYPE" expression="complete()" defaultValue="&quot;type&quot;" alwaysStopAt="true" />
        <context>
          <option name="GO" value="true" />
        </context>
      </template>
      <template name="vdm" value="func $TYPE$Validate(m *$PKG$.$TYPE$) error {&#10;&#9;var err error&#10;&#9;err = valid.Validate(m, valid.NotNil)&#10;&#9;if err != nil {&#10;&#9;&#9;return err&#10;&#9;}&#10;&#9;err = valid.ValidateStruct(m,&#10;&#9;    // todo&#10;&#9;&#9;valid.Field(&amp;m.filedName, valid.Required),&#10;&#9;)&#10;&#9;if err != nil {&#10;&#9;&#9;return err&#10;&#9;}&#10;&#9;return nil&#10;}&#10;" description="validate model" toReformat="false" toShortenFQNames="true">
        <variable name="PKG" expression="complete()" defaultValue="&quot;pkg&quot;" alwaysStopAt="true" />
        <variable name="TYPE" expression="" defaultValue="" alwaysStopAt="true" />
        <context>
          <option name="GO" value="true" />
        </context>
      </template>
    </templateSet>
    
    <!-- Go Struct Tags.xml -->
    <templateSet group="Go Struct Tags">
      <template name="`env" value="`env:&quot;$FIELD_NAME$&quot;$END$" description="`env:&quot;&quot;`" toReformat="true" toShortenFQNames="true">
        <variable name="FIELD_NAME" expression="capitalizeAndUnderscore(fieldName())" defaultValue="" alwaysStopAt="true" />
        <context>
          <option name="GO_TAG" value="true" />
          <option name="GO_TAG_LITERAL" value="true" />
        </context>
      </template>
      <template name="env" value="env:&quot;$FIELD_NAME$&quot;$END$" description="env:&quot;&quot;" toReformat="false" toShortenFQNames="true">
        <variable name="FIELD_NAME" expression="capitalizeAndUnderscore(fieldName())" defaultValue="" alwaysStopAt="true" />
        <context>
          <option name="GO_TAG_LITERAL" value="true" />
        </context>
      </template>
    </templateSet>
    
  • 相关阅读:
    06 Python类、对象
    05 Python DNS域名轮询业务监控
    04 Python字符串
    03 Python函数
    02 Python循环
    执行config文件时,config.log中报错xcrun: error: active developer path ("/Applications/Xcode.app/Contents/Developer") does not exist, use xcode-select to change
    浏览我的php网页时,出现的都是网页的代码
    php
    [转载]用c写PHP的扩展接口(php5,c++)
    PHP扩展编写示例
  • 原文地址:https://www.cnblogs.com/XiaoXiaoShuai-/p/15315195.html
Copyright © 2020-2023  润新知