Skip to content

Commit

Permalink
Fix AnalyzeFile if comment is in the same line as code.
Browse files Browse the repository at this point in the history
  • Loading branch information
kuba-- committed May 31, 2019
1 parent 1429cba commit 1ea0457
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 118 deletions.
180 changes: 81 additions & 99 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"os"
"strings"
"unicode"
)

type ClocFile struct {
Expand Down Expand Up @@ -53,146 +54,127 @@ func AnalyzeReader(filename string, language *Language, file io.Reader, opts *Cl
}

isFirstLine := true
isInComments := false
isInCommentsSame := false
inComments := [][2]string{}
buf := getByteSlice()
defer putByteSlice(buf)
scanner := bufio.NewScanner(file)
scanner.Buffer(buf, 1024*1024)

scannerloop:
for scanner.Scan() {
lineOrg := scanner.Text()
line := strings.TrimSpace(lineOrg)

if len(strings.TrimSpace(line)) == 0 {
clocFile.Blanks++
if opts.OnBlank != nil {
opts.OnBlank(line)
}

if opts.Debug {
fmt.Printf("[BLNK,cd:%d,cm:%d,bk:%d,iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
onBlank(clocFile, opts, len(inComments) > 0, line, lineOrg)
continue
}

// shebang line is 'code'
if isFirstLine && strings.HasPrefix(line, "#!") {
clocFile.Code++
if opts.OnCode != nil {
opts.OnCode(line)
}

onCode(clocFile, opts, len(inComments) > 0, line, lineOrg)
isFirstLine = false
if opts.Debug {
fmt.Printf("[CODE,cd:%d,cm:%d,bk:%d,iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
continue
}

if len(language.lineComments) > 0 {
isSingleComment := false
if len(inComments) == 0 {
if isFirstLine {
line = trimBOM(line)
}

singleloop:
for _, singleComment := range language.lineComments {
if strings.HasPrefix(line, singleComment) {
clocFile.Comments++
if opts.OnComment != nil {
opts.OnComment(line)
// check if single comment is a prefix of multi comment
for _, ml := range language.multiLines {
if strings.HasPrefix(line, ml[0]) {
break singleloop
}
}

isSingleComment = true
break
onComment(clocFile, opts, len(inComments) > 0, line, lineOrg)
continue scannerloop
}
}
if isSingleComment {
if opts.Debug {
fmt.Printf("[COMM,cd:%d,cm:%d,bk:%d,iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
continue

if len(language.multiLines) == 0 {
onCode(clocFile, opts, len(inComments) > 0, line, lineOrg)
continue scannerloop
}
}

if len(inComments) == 0 && !containsComment(line, language.multiLines) {
onCode(clocFile, opts, len(inComments) > 0, line, lineOrg)
continue scannerloop
}

isCode := false
multiLine := ""
multiLineEnd := ""
for i := range language.multiLines {
multiLine = language.multiLines[i][0]
multiLineEnd = language.multiLines[i][1]
if multiLine != "" {
if strings.HasPrefix(line, multiLine) {
isInComments = true
} else if strings.HasSuffix(line, multiLineEnd) {
isInComments = true
} else if containComments(line, multiLine, multiLineEnd) {
isInComments = true
if (multiLine != multiLineEnd) &&
(strings.HasSuffix(line, multiLine) || strings.HasPrefix(line, multiLineEnd)) {
clocFile.Code++
if opts.OnCode != nil {
opts.OnCode(line)
}
lenLine := len(line)
for pos := 0; pos < lenLine; {
for _, ml := range language.multiLines {
begin, end := ml[0], ml[1]
lenBegin := len(begin)

if pos+lenBegin <= lenLine && strings.HasPrefix(line[pos:], begin) && (begin != end || len(inComments) == 0) {
pos += lenBegin
inComments = append(inComments, [2]string{begin, end})
continue
}

isCode = true
if opts.Debug {
fmt.Printf("[CODE,cd:%d,cm:%d,bk:%d,iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
continue
if n := len(inComments); n > 0 {
last := inComments[n-1]
if pos+len(last[1]) <= lenLine && strings.HasPrefix(line[pos:], last[1]) {
inComments = inComments[:n-1]
pos += len(last[1])
}
}
if isInComments {
break
} else if pos < lenLine && !unicode.IsSpace(nextRune(line[pos:])) {
isCode = true
}
}
pos++
}

if isInComments && isCode {
continue
if isCode {
onCode(clocFile, opts, len(inComments) > 0, line, lineOrg)
} else {
onComment(clocFile, opts, len(inComments) > 0, line, lineOrg)
}
}

if isInComments {
if multiLine == multiLineEnd {
if strings.Count(line, multiLineEnd) == 2 {
isInComments = false
isInCommentsSame = false
} else if strings.HasPrefix(line, multiLineEnd) ||
strings.HasSuffix(line, multiLineEnd) {
if isInCommentsSame {
isInComments = false
}
isInCommentsSame = !isInCommentsSame
}
} else {
if strings.Contains(line, multiLineEnd) {
isInComments = false
}
}
clocFile.Comments++
if opts.OnComment != nil {
opts.OnComment(line)
}
return clocFile
}

if opts.Debug {
fmt.Printf("[COMM,cd:%d,cm:%d,bk:%d,iscm:%v,iscms:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, isInCommentsSame, lineOrg)
}
continue
}
func onBlank(clocFile *ClocFile, opts *ClocOptions, isInComments bool, line, lineOrg string) {
clocFile.Blanks++
if opts.OnBlank != nil {
opts.OnBlank(line)
}

clocFile.Code++
if opts.OnCode != nil {
opts.OnCode(line)
}
if opts.Debug {
fmt.Printf("[BLNK, cd:%d, cm:%d, bk:%d, iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
}

if opts.Debug {
fmt.Printf("[CODE,cd:%d,cm:%d,bk:%d,iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
func onComment(clocFile *ClocFile, opts *ClocOptions, isInComments bool, line, lineOrg string) {
clocFile.Comments++
if opts.OnComment != nil {
opts.OnComment(line)
}

return clocFile
if opts.Debug {
fmt.Printf("[COMM, cd:%d, cm:%d, bk:%d, iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
}

func onCode(clocFile *ClocFile, opts *ClocOptions, isInComments bool, line, lineOrg string) {
clocFile.Code++
if opts.OnCode != nil {
opts.OnCode(line)
}

if opts.Debug {
fmt.Printf("[CODE, cd:%d, cm:%d, bk:%d, iscm:%v] %s\n",
clocFile.Code, clocFile.Comments, clocFile.Blanks, isInComments, lineOrg)
}
}
118 changes: 118 additions & 0 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,45 @@ class A:
}
}

func TestAnalayzeFile4PythonNoShebang(t *testing.T) {
tmpfile, err := ioutil.TempFile("", "tmp.py")
if err != nil {
t.Logf("ioutil.TempFile() error. err=[%v]", err)
return
}
defer os.Remove(tmpfile.Name())

tmpfile.Write([]byte(`a = '''hello
world
'''
b = 1
"""hello
commen
"""
print a, b
`))

language := NewLanguage("Python", []string{"#"}, [][]string{{"\"\"\"", "\"\"\""}})
clocOpts := NewClocOptions()
clocFile := AnalyzeFile(tmpfile.Name(), language, clocOpts)
tmpfile.Close()

if clocFile.Blanks != 2 {
t.Errorf("invalid logic. blanks=%v", clocFile.Blanks)
}
if clocFile.Comments != 3 {
t.Errorf("invalid logic. comments=%v", clocFile.Comments)
}
if clocFile.Code != 5 {
t.Errorf("invalid logic. code=%v", clocFile.Code)
}
if clocFile.Lang != "Python" {
t.Errorf("invalid logic. lang=%v", clocFile.Lang)
}
}

func TestAnalayzeFile4Go(t *testing.T) {
tmpfile, err := ioutil.TempFile("", "tmp.go")
if err != nil {
Expand Down Expand Up @@ -191,6 +230,85 @@ func main() {
}
}

func TestAnalyzeFile4GoWithNoComment(t *testing.T) {
tmpfile, err := ioutil.TempFile("", "tmp.go")
if err != nil {
t.Logf("ioutil.TempFile() error. err=[%v]", err)
return
}
defer os.Remove(tmpfile.Name())

tmpfile.Write([]byte(`package main
func main() {
a := "/* */"
b := "// "
}
`))

language := NewLanguage("Go", []string{"//"}, [][]string{{"/*", "*/"}})
clocOpts := NewClocOptions()
clocFile := AnalyzeFile(tmpfile.Name(), language, clocOpts)
tmpfile.Close()

if clocFile.Blanks != 1 {
t.Errorf("invalid logic. blanks=%v", clocFile.Blanks)
}
if clocFile.Comments != 0 {
t.Errorf("invalid logic. comments=%v", clocFile.Comments)
}
if clocFile.Code != 5 {
t.Errorf("invalid logic. code=%v", clocFile.Code)
}
if clocFile.Lang != "Go" {
t.Errorf("invalid logic. lang=%v", clocFile.Lang)
}
}

func TestAnalyzeFile4JavaWithCommentInCodeLine(t *testing.T) {
tmpfile, err := ioutil.TempFile("", "tmp.java")
if err != nil {
t.Logf("ioutil.TempFile() error. err=[%v]", err)
return
}
defer os.Remove(tmpfile.Name())

tmpfile.Write([]byte(`public class Sample {
public static void main(String args[]){
int a; /* A takes care of counts */
int b;
int c;
String d; /*Just adding comments */
bool e; /*
comment*/
bool f; /*
comment1
comment2
*/
/*End of Main*/
}
}
`))

language := NewLanguage("Java", []string{"//"}, [][]string{{"/*", "*/"}})
clocOpts := NewClocOptions()
clocFile := AnalyzeFile(tmpfile.Name(), language, clocOpts)
tmpfile.Close()

if clocFile.Blanks != 0 {
t.Errorf("invalid logic. blanks=%v", clocFile.Blanks)
}
if clocFile.Comments != 5 {
t.Errorf("invalid logic. comments=%v", clocFile.Comments)
}
if clocFile.Code != 10 {
t.Errorf("invalid logic. code=%v", clocFile.Code)
}
if clocFile.Lang != "Java" {
t.Errorf("invalid logic. lang=%v", clocFile.Lang)
}
}

func TestAnalayzeReader(t *testing.T) {
buf := bytes.NewBuffer([]byte(`#!/bin/python
Expand Down
Loading

0 comments on commit 1ea0457

Please sign in to comment.