映えるGoコード

CSSで柔軟にカスタマイズできるコードハイライトツールの紹介

Dennis Metzger
golang.tokyo #42

Dennis(デニス) Metzger(メッツガー)

Backend Engineer @ Finatext

					
						package main

						import "fmt"

						func main() {
							ch := make(chan float64)
							ch <- 1.0e10 // magic number
							x, ok := <-ch
							defer fmt.Println(`exiting now`)
							go println(len("hello world!"))
							return
						}
					
					
					
						package main

						import "fmt"

						func main() {
							ch := make(chan float64)
							ch <- 1.0e10 // magic number
							x, ok := <-ch
							defer fmt.Println(`exiting now`)
							go println(len("hello world!"))
							return
						}
					
					
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
					
						<pre>
							<code class="language-go">
								package main

								import "fmt"

								func main() {
									ch := make(chan float64)
									ch <- 1.0e10 // magic number
									x, ok := <-ch
									defer fmt.Println(`exiting now`)
									go println(len("hello world!"))
									return
								}
							</code>
						</pre>
					
					
					
						
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan float64)
  5. ch <- 1.0e10 // magic number
  6. x, ok := <-ch
  7. defer fmt.Println(`exiting now`)
  8. go println(len("hello world!"))
  9. return
  10. }

完成までの道のり

❯ gopls semtok ./main.go
  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("Hello, World!")
  5. }
  1. /*⇒7,keyword,[]*/package /*⇒4,namespace,[]*/main
  2. /*⇒6,keyword,[]*/import "fmt"/*⇐3,namespace,[]*/
  3. /*⇒4,keyword,[]*/func /*⇒4,function,[definition signature]*/main() {
  4. /*⇒3,namespace,[]*/fmt./*⇒7,function,[signature]*/Println(/*⇒15,string,[]*/"Hello, World!")
  5. }
/*⇒4,function,[definition signature]*/main
必要なコードを切り出し、
実行してみると……
❯ echo a := 1 > test.go

❯ go run ./cmd/prettygo ./test.go


ん?
❯ gopls semtok ./test.go


ん??

go/parser/parser.go から

func (p *parser) parseFile() *ast.File { // 中略 // Don't bother parsing the rest if we // had errors scanning the first token. // Likely not a Go source file at all. if p.errors.Len() != 0 { return nil }

go/parser/parser.go から

// 中略 pos := p.expect(token.PACKAGE) // 中略 // Don't bother parsing the rest if we // had errors parsing the package clause. // Likely not a Go source file at all. if p.errors.Len() != 0 { return nil }

go/parser/parser.go から

func (p *parser) expect(/*中略*/) token.Pos { pos := p.pos if p.tok != tok { p.errorExpected(/*中略*/) } else { p.firstTokenFound = true } if p.firstTokenFound { p.next() // make progress } return pos }

go/parser/parser.go から

  1. func (p *parser) expect(/*中略*/) token.Pos {
  2. pos := p.pos
  3. if p.tok != tok {
  4. p.errorExpected(/*中略*/)
  5. } else {
  6. p.firstTokenFound = true
  7. }
  8. if p.firstTokenFound {
  9. p.next() // make progress
  10. }
  11. return pos
  12. }

AST に足りない要素を追加






a := 1

  1. package main
  2. func init() {}
  3. func main() {
  4. a := 1
  5. }
良い感じにできたと思ったら⋯⋯

🚨バグ発見🚨

  1. str := `
  2. test`
  1. str := `\r\n
  2. \r\n
  3. \r\n
  4. \r\n
  5. \r\n
  6. test`
  1. str := /*⇒1,string,[]*/`
  2. /*⇒0,string,[]*/
  3. /*⇒0,string,[]*/
  4. /*⇒0,string,[]*/
  5. /*⇒1,string,[]*/test`
https://github.com/golang/go/issues/75969

で報告し、対応済み

完成

以上、prettygo の仕組みでした!

ぜひブログスライド
ご活用ください

PRもお待ちしています!

https://github.com/sollniss/prettygo