My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more
Lines of code - Golang

Lines of code - Golang

Count Golang lines of code without test files.

Bajro's photo
Bajro
·Nov 10, 2021·

4 min read

Let's make a simple counter to check how many lines of code are in our project.

I want to start with a simple function.

func LineCounter(r string) (int, error) {

    var count int
    rr  := strings.Split(r, "\n")
    var rrr []string
    cc := 0
    comstart := false
    for _, str := range rr {
        if strings.HasPrefix(str, "/*")   {
            comstart = true
            continue
        }
        if strings.Contains(str, "*/") {
            comstart = false
            cc += 1

        }
        if (str != "" && !strings.HasPrefix(str, "//") && !comstart) {
            rrr = append(rrr, str)
        }
    }

    count = len(rrr) - cc

    return count, nil
}

This function takes some text and split all lines by "\n" (newline sign). We check if 2 things if the line is empty and also if the line is any kind of comment. In golang there is a single-line comment starting with // and also multiline starting with / and end with /. The function return number of lines or error.

var (
    Info = Teal
    Warn = Yellow
    Fata = Red
  )

  var (
    Black   = Color("\033[1;30m%s\033[0m")
    Red     = Color("\033[1;31m%s\033[0m")
    Green   = Color("\033[1;32m%s\033[0m")
    Yellow  = Color("\033[1;33m%s\033[0m")
    Purple  = Color("\033[1;34m%s\033[0m")
    Magenta = Color("\033[1;35m%s\033[0m")
    Teal    = Color("\033[1;36m%s\033[0m")
    White   = Color("\033[1;37m%s\033[0m")
  )

  func Color(colorString string) func(...interface{}) string {
    sprint := func(args ...interface{}) string {
      return fmt.Sprintf(colorString,
        fmt.Sprint(args...))
    }
    return sprint
  }

This part of the code we use to create colored text doesn't need explanation I guess.

Last but not least we have the main function.

func main() {
    arg := os.Args[1]
    all := 0

    err := filepath.Walk(arg, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if (!info.IsDir()  && !strings.Contains(info.Name(), "test") ){
            ext := filepath.Ext(path)


            if ext == ".go" {

            jj, _ := os.ReadFile(path)
            g, _ := LineCounter(string(jj))
            all += g
            var u string
            if g < 200 {
                z := strconv.Itoa(g)
                u = Info(z)
            } 
            if g >= 200 && g < 1000 {
                z := strconv.Itoa(g)
                u = Warn(z)
            }
            if g >= 1000 {
                z := strconv.Itoa(g)
                u = Fata(z)
            }
            fmt.Printf("File %s have %s lines of code \n",path, u)
            }
        }

        return nil
    })
    if err != nil {
        log.Println(err)
    }
    fmt.Println("All lines", all)

}

Here I get my path from the console and go through it recursively checking if the path is not a folder. If the path is not a folder (it is a file) and if the file has ".go" extension. I also check if the filename contains "test" word. I don't want to count test files (In golang test files must contain test after the name). I add to sum every time when our LineCounter function returns a number. I check if a number of lines are less than 200 converts the number to a string and make it teal, 200-1000 Yellow, and everything more than 1000 is red. I also print every file with a number of lines. And on end, I print the sum of all lines.

Finally our completed code.

package main

import (
    "fmt"
    "log"
    "os"
    "path/filepath"
    "strconv"
    "strings"
)

var (
    Info = Teal
    Warn = Yellow
    Fata = Red
  )

  var (
    Black   = Color("\033[1;30m%s\033[0m")
    Red     = Color("\033[1;31m%s\033[0m")
    Green   = Color("\033[1;32m%s\033[0m")
    Yellow  = Color("\033[1;33m%s\033[0m")
    Purple  = Color("\033[1;34m%s\033[0m")
    Magenta = Color("\033[1;35m%s\033[0m")
    Teal    = Color("\033[1;36m%s\033[0m")
    White   = Color("\033[1;37m%s\033[0m")
  )

  func Color(colorString string) func(...interface{}) string {
    sprint := func(args ...interface{}) string {
      return fmt.Sprintf(colorString,
        fmt.Sprint(args...))
    }
    return sprint
  }

func main() {
    arg := os.Args[1]
    all := 0

    err := filepath.Walk(arg, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if (!info.IsDir()  && !strings.Contains(info.Name(), "test") ){
            ext := filepath.Ext(path)


            if ext == ".go" {

            jj, _ := os.ReadFile(path)
            g, _ := LineCounter(string(jj))
            all += g
            var u string
            if g < 200 {
                z := strconv.Itoa(g)
                u = Info(z)
            } 
            if g >= 200 && g < 1000 {
                z := strconv.Itoa(g)
                u = Warn(z)
            }
            if g >= 1000 {
                z := strconv.Itoa(g)
                u = Fata(z)
            }
            fmt.Printf("File %s have %s lines of code \n",path, u)

            }
        }

        return nil
    })
    if err != nil {
        log.Println(err)
    }
    fmt.Println("All lines", all)

}


func LineCounter(r string) (int, error) {

    var count int
    rr  := strings.Split(r, "\n")
    var rrr []string
    cc := 0
    comstart := false
    for _, str := range rr {
        if strings.HasPrefix(str, "/*")   {
            comstart = true
            continue
        }
        if strings.Contains(str, "*/") {
            comstart = false
            cc += 1

        }
        if (str != "" && !strings.HasPrefix(str, "//") && !comstart) {
            rrr = append(rrr, str)
        }
    }

    count = len(rrr) - cc

    return count, nil
}

I hope it will help some of you please tell me how it works