Repository: reviewdog/reviewdog
Pull request #450 (2020-01-19 19:55:34)

Filter check results by all lines from diff by default
Current behaviour:
When reviewdog filters the check results by diff, which is either provided by cli option or taken from Gitbub or Gitlab PR/MR, it does so only taking into account the lines marked as added in the diff. This has certain downsides described in #187.

New behaviour:
Filter check results by all the lines from diff but add the option to force the existing logic of filtering only by added diff lines.


Patch:
diff --git a/.git_hooks/README.md b/.git_hooks/README.md
deleted file mode 100644
index ac6e6d0e..00000000
--- a/.git_hooks/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-## .git_hooks - Example reviewdog integration with git hooks
-
-## Setup
-
-```shell
-git config core.hooksPath .git_hooks
-```
-
-## [prepare-commit-msg](prepare-commit-msg)
-
-![prepare-commit-msg example](https://user-images.githubusercontent.com/3797062/72665542-903c0700-3a4c-11ea-9d02-d65cc851f085.png)
diff --git a/.git_hooks/prepare-commit-msg b/.git_hooks/prepare-commit-msg
deleted file mode 100755
index 7168b3f9..00000000
--- a/.git_hooks/prepare-commit-msg
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-#
-# Append linter results at the bottom of commit message to check results on
-# commit.
-echo -e "# ------------------------ >8 ------------------------" >> $1
-echo -e "# reviewdog results" >> $1
-golangci-lint run --out-format=line-number --fast ./... | reviewdog -f=golangci-lint -diff="git diff @" >> $1
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7bc9f883..8aeb5b3b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,8 +5,6 @@ stages:
 
 # https://github.com/tmaier/gitlab-auto-merge-request
 Open Merge Request:
-  only:
-    - branches
   except:
     - master
   stage: openMr
@@ -18,22 +16,15 @@ Open Merge Request:
     - merge-request.sh
 
 Reviewdog:
-  only:
-    - branches
   except:
     - master
   stage: reviewdog
   image: golang:latest
-  cache:
-    key:
-      files:
-        - go.sum
-    paths:
-      - .cache
   before_script:
     - export GO111MODULE=on
-    - export GOPATH=${CI_PROJECT_DIR}/.cache
-    - export PATH=$PATH:$GOPATH/bin
+    # Set up $GOPATH and install reviewdog (Note that main repo is github.com)
+    - ln -s /builds /go/src/github.com
+    - cd /go/src/github.com/${CI_PROJECT_PATH}
     - go install ./cmd/reviewdog
     - ( cd linters && go get golang.org/x/lint/golint )
   script:
diff --git a/README.md b/README.md
index 4f62914f..c41afc73 100644
--- a/README.md
+++ b/README.md
@@ -13,16 +13,13 @@
     <img alt="LICENSE" src="https://img.shields.io/badge/license-MIT-blue.svg?maxAge=43200">
   </a>
   <a href="https://godoc.org/github.com/reviewdog/reviewdog">
-    <img alt="GoDoc" src="https://img.shields.io/badge/godoc-reference-4F73B3.svg?label=godoc.org&maxAge=43200&logo=go">
+    <img alt="GoDoc" src="https://img.shields.io/badge/godoc-reference-4F73B3.svg?label=godoc.org&maxAge=43200">
   </a>
   <a href="https://github.com/reviewdog/reviewdog/releases">
-    <img alt="releases" src="https://img.shields.io/github/release/reviewdog/reviewdog.svg?logo=github">
+    <img alt="releases" src="https://img.shields.io/github/release/reviewdog/reviewdog.svg?maxAge=43200">
   </a>
-  <a href="https://somsubhra.com/github-release-stats/?username=reviewdog&repository=reviewdog">
-    <img alt="Github Releases Stats" src="https://img.shields.io/github/downloads/reviewdog/reviewdog/total.svg?logo=github">
-  </a>
-  <a href="https://gitlab.com/reviewdog/reviewdog/pipelines">
-    <img alt="GitLab Supported" src="https://img.shields.io/badge/GitLab%20-Supported-fc6d26?logo=gitlab">
+  <a href="https://github.com/reviewdog/reviewdog/releases">
+    <img alt="Github All Releases" src="https://img.shields.io/github/downloads/reviewdog/reviewdog/total.svg?maxAge=43200">
   </a>
 </p>
 
@@ -33,10 +30,10 @@
   <a href="https://github.com/reviewdog/reviewdog/actions?query=workflow%3Areviewdog+event%3Apush+branch%3Amaster">
     <img alt="reviewdog" src="https://github.com/reviewdog/reviewdog/workflows/reviewdog/badge.svg?branch=master&event=push">
   </a>
-  <a href="https://travis-ci.org/reviewdog/reviewdog"><img alt="Travis Status" src="https://img.shields.io/travis/reviewdog/reviewdog/master.svg?label=Travis&logo=travis"></a>
-  <a href="https://circleci.com/gh/reviewdog/reviewdog"><img alt="CircleCI Status" src="http://img.shields.io/circleci/build/github/reviewdog/reviewdog/master.svg?label=CircleCI&logo=circleci"></a>
-  <a href="https://codecov.io/github/reviewdog/reviewdog"><img alt="Coverage Status" src="https://img.shields.io/codecov/c/github/reviewdog/reviewdog/master.svg?logo=codecov"></a>
-  <a href="https://starcharts.herokuapp.com/reviewdog/reviewdog"><img alt="Stars" src="https://img.shields.io/github/stars/reviewdog/reviewdog.svg?style=social"></a>
+  <a href="https://travis-ci.org/reviewdog/reviewdog"><img alt="Travis Status" src="https://img.shields.io/travis/reviewdog/reviewdog/master.svg?label=travis&maxAge=43200"></a>
+  <a href="https://circleci.com/gh/reviewdog/reviewdog"><img alt="CircleCI Status" src="https://img.shields.io/circleci/project/github/reviewdog/reviewdog/master.svg?label=circle&maxAge=43200"></a>
+  <a href="https://codecov.io/github/reviewdog/reviewdog"><img alt="Coverage Status" src="https://img.shields.io/codecov/c/github/reviewdog/reviewdog/master.svg?maxAge=43200"></a>
+  <a href="https://starcharts.herokuapp.com/reviewdog/reviewdog"><img alt="Stars" src="https://img.shields.io/github/stars/reviewdog/reviewdog.svg?style=social&maxAge=43200"></a>
 </p>
 
 "reviewdog" provides a way to post review comments to code hosting service,
@@ -450,8 +447,6 @@ You can also use public GitHub Actions to start using reviewdog with ease! :tada
   - [reviewdog/action-misspell](https://github.com/reviewdog/action-misspell) - Run [misspell](https://github.com/client9/misspell).
   - [tsuyoshicho/action-textlint](https://github.com/tsuyoshicho/action-textlint) - Run [textlint](https://github.com/textlint/textlint)
   - [tsuyoshicho/action-redpen](https://github.com/tsuyoshicho/action-redpen) - Run [redpen](https://github.com/redpen-cc/redpen)
-- Docker
-  - [reviewdog/action-hadolint](https://github.com/reviewdog/action-hadolint) - Run [hadolint](https://github.com/hadolint/hadolint) to lint `Dockerfile`.
 - Env
   - [mgrachev/action-dotenv-linter](https://github.com/mgrachev/action-dotenv-linter) - Run [dotenv-linter](https://github.com/mgrachev/dotenv-linter) to lint `.env` files.
 - Shell script
@@ -467,10 +462,7 @@ You can also use public GitHub Actions to start using reviewdog with ease! :tada
   - [tsuyoshicho/action-vimlint](https://github.com/tsuyoshicho/action-vimlint) - Run [vim-vimlint](https://github.com/syngan/vim-vimlint)
 - Terraform
   - [reviewdog/action-tflint](https://github.com/reviewdog/action-tflint) - Run [tflint](https://github.com/wata727/tflint).
-- YAML
-  - [reviewdog/action-yamllint](https://github.com/reviewdog/action-yamllint) - Run [yamllint](https://github.com/adrienverge/yamllint).
 - Ruby
-  - [reviewdog/action-brakeman](https://github.com/reviewdog/action-brakeman) - Run [brakeman](https://github.com/presidentbeef/brakeman).
   - [reviewdog/action-rubocop](https://github.com/reviewdog/action-rubocop) - Run [rubocop](https://github.com/rubocop-hq/rubocop).
 - Python
   - [wemake-python-styleguide](https://github.com/wemake-services/wemake-python-styleguide) - Run wemake-python-styleguide
diff --git a/cmd/reviewdog/doghouse.go b/cmd/reviewdog/doghouse.go
index 59b7e1c9..0f5c402f 100644
--- a/cmd/reviewdog/doghouse.go
+++ b/cmd/reviewdog/doghouse.go
@@ -39,7 +39,7 @@ func runDoghouse(ctx context.Context, r io.Reader, w io.Writer, opt *option, isP
 	if err != nil {
 		return err
 	}
-	filteredResultSet, err := postResultSet(ctx, resultSet, ghInfo, cli, forPr)
+	filteredResultSet, err := postResultSet(ctx, resultSet, ghInfo, cli, forPr, opt.filterMode)
 	if err != nil {
 		return err
 	}
@@ -109,7 +109,7 @@ func checkResultSet(ctx context.Context, r io.Reader, opt *option, isProject boo
 }
 
 func postResultSet(ctx context.Context, resultSet *reviewdog.ResultMap,
-	ghInfo *cienv.BuildInfo, cli client.DogHouseClientInterface, forPr bool) (*reviewdog.FilteredResultMap, error) {
+	ghInfo *cienv.BuildInfo, cli client.DogHouseClientInterface, forPr bool, filterMode reviewdog.FilterMode) (*reviewdog.FilteredResultMap, error) {
 	var g errgroup.Group
 	wd, _ := os.Getwd()
 	filteredResultSet := new(reviewdog.FilteredResultMap)
@@ -130,6 +130,7 @@ func postResultSet(ctx context.Context, resultSet *reviewdog.ResultMap,
 			Level:       result.Level,
 			// If it's only for PR, do not report results outside diff.
 			OutsideDiff: !forPr,
+			FilterMode:  filterMode,
 		}
 		g.Go(func() error {
 			res, err := cli.Check(ctx, req)
diff --git a/cmd/reviewdog/main.go b/cmd/reviewdog/main.go
index c525498e..c2edb8f5 100644
--- a/cmd/reviewdog/main.go
+++ b/cmd/reviewdog/main.go
@@ -54,6 +54,7 @@ type option struct {
 	level            string
 	guessPullRequest bool
 	tee              bool
+	filterMode       reviewdog.FilterMode
 }
 
 // flags doc
@@ -70,7 +71,13 @@ const (
 	levelDoc            = `report level currently used for github-pr-check reporter ("info","warning","error").`
 	guessPullRequestDoc = `guess Pull Request ID by branch name and commit SHA`
 	teeDoc              = `enable "tee"-like mode which outputs tools's output as is while reporting results to -reporter. Useful for debugging as well.`
-	reporterDoc         = `reporter of reviewdog results. (local, github-check, github-pr-check, github-pr-review, gitlab-mr-discussion, gitlab-mr-commit)
+	filterModeDoc       = `how to filter checks results. Possible values are: diff_context, added.
+    "diff_context" (default)
+        Filter by diff context, which can include unchanged lines.
+    "added"
+        Filter by added diff lines.
+`
+	reporterDoc = `reporter of reviewdog results. (local, github-check, github-pr-check, github-pr-review, gitlab-mr-discussion, gitlab-mr-commit)
 	"local" (default)
 		Report results to stdout.
 
@@ -156,6 +163,7 @@ func init() {
 	flag.StringVar(&opt.level, "level", "error", levelDoc)
 	flag.BoolVar(&opt.guessPullRequest, "guess", false, guessPullRequestDoc)
 	flag.BoolVar(&opt.tee, "tee", false, teeDoc)
+	flag.Var(&opt.filterMode, "filter-mode", filterModeDoc)
 }
 
 func usage() {
@@ -300,7 +308,7 @@ github-pr-check reporter as a fallback.
 		if err != nil {
 			return err
 		}
-		return project.Run(ctx, conf, buildRunnersMap(opt.runners), cs, ds, opt.tee)
+		return project.Run(ctx, conf, buildRunnersMap(opt.runners), cs, ds, opt.tee, opt.filterMode)
 	}
 
 	p, err := newParserFromOpt(opt)
@@ -308,7 +316,7 @@ github-pr-check reporter as a fallback.
 		return err
 	}
 
-	app := reviewdog.NewReviewdog(toolName(opt), p, cs, ds)
+	app := reviewdog.NewReviewdog(toolName(opt), p, cs, ds, opt.filterMode)
 	return app.Run(ctx, r)
 }
 
diff --git a/doghouse/server/doghouse.go b/doghouse/server/doghouse.go
index 28f0eb2b..3cb47672 100644
--- a/doghouse/server/doghouse.go
+++ b/doghouse/server/doghouse.go
@@ -49,8 +49,7 @@ func (ch *Checker) Check(ctx context.Context) (*doghouse.CheckResponse, error) {
 	}
 
 	results := annotationsToCheckResults(ch.req.Annotations)
-	filtered := reviewdog.FilterCheck(results, filediffs, 1, "")
-
+	filtered := reviewdog.FilterCheck(results, filediffs, 1, "", ch.req.FilterMode)
 	check, err := ch.createCheck(ctx)
 	if err != nil {
 		// If this error is StatusForbidden (403) here, it means reviewdog is
diff --git a/doghouse/service.go b/doghouse/service.go
index 4c41f65e..4281ecb9 100644
--- a/doghouse/service.go
+++ b/doghouse/service.go
@@ -40,6 +40,10 @@ type CheckRequest struct {
 	// (because there are no diff!).
 	// Optional.
 	OutsideDiff bool `json:"outside_diff"`
+
+	// FilterMode represents a way to filter checks results
+	// Optional.
+	FilterMode reviewdog.FilterMode `json:"filter_mode"`
 }
 
 // CheckResponse represents doghouse GitHub check response.
diff --git a/filter.go b/filter.go
index 426af308..c79ef767 100644
--- a/filter.go
+++ b/filter.go
@@ -7,6 +7,42 @@ import (
 	"github.com/reviewdog/reviewdog/diff"
 )
 
+// FilterMode represents enumeration of available filter modes
+type FilterMode int
+
+const (
+	// FilterModeDiffContext represents filtering by diff context
+	FilterModeDiffContext FilterMode = iota
+	// FilterModeAdded represents filtering by added diff lines
+	FilterModeAdded
+)
+
+// String implements the flag.Value interface
+func (mode *FilterMode) String() string {
+	names := [...]string{
+		"diff_context",
+		"added"}
+
+	if *mode < FilterModeDiffContext || *mode > FilterModeAdded {
+		return "Unknown"
+	}
+
+	return names[*mode]
+}
+
+// Set implements the flag.Value interface
+func (mode *FilterMode) Set(value string) error {
+	switch value {
+	case "diff_context":
+		*mode = FilterModeDiffContext
+	case "added":
+		*mode = FilterModeAdded
+	default:
+		*mode = FilterModeDiffContext
+	}
+	return nil
+}
+
 // FilteredCheck represents CheckResult with filtering info.
 type FilteredCheck struct {
 	*CheckResult
@@ -16,19 +52,28 @@ type FilteredCheck struct {
 
 // FilterCheck filters check results by diff. It doesn't drop check which
 // is not in diff but set FilteredCheck.InDiff field false.
-func FilterCheck(results []*CheckResult, diff []*diff.FileDiff, strip int, wd string) []*FilteredCheck {
+func FilterCheck(results []*CheckResult, diff []*diff.FileDiff, strip int, wd string, filterMode FilterMode) []*FilteredCheck {
 	checks := make([]*FilteredCheck, 0, len(results))
 
-	addedlines := addedDiffLines(diff, strip)
+	var filterFn lineComparator
+
+	switch filterMode {
+	case FilterModeDiffContext:
+		filterFn = anyLine
+	case FilterModeAdded:
+		filterFn = isAddedLine
+	}
+
+	significantlines := significantDiffLines(diff, filterFn, strip)
 
 	for _, result := range results {
 		check := &FilteredCheck{CheckResult: result}
 
-		addedline := addedlines.Get(result.Path, result.Lnum)
+		significantline := significantlines.Get(result.Path, result.Lnum)
 		result.Path = CleanPath(result.Path, wd)
-		if addedline != nil {
+		if significantline != nil {
 			check.InDiff = true
-			check.LnumDiff = addedline.LnumDiff
+			check.LnumDiff = significantline.LnumDiff
 		}
 
 		checks = append(checks, check)
@@ -54,18 +99,18 @@ func CleanPath(path, workdir string) string {
 	return filepath.ToSlash(p)
 }
 
-// addedLine represents added line in diff.
-type addedLine struct {
+// significantLine represents the line in diff we want to filter check results by.
+type significantLine struct {
 	Path     string // path to new file
 	Lnum     int    // the line number in the new file
 	LnumDiff int    // the line number of the diff (Same as Lnumdiff of diff.Line)
 	Content  string // line content
 }
 
-// posToAddedLine is a hash table of normalized path to line number to addedLine.
-type posToAddedLine map[string]map[int]*addedLine
+// posToSignificantLine is a hash table of normalized path to line number to significantLine.
+type posToSignificantLine map[string]map[int]*significantLine
 
-func (p posToAddedLine) Get(path string, lnum int) *addedLine {
+func (p posToSignificantLine) Get(path string, lnum int) *significantLine {
 	npath, err := normalizePath(path)
 	if err != nil {
 		return nil
@@ -81,12 +126,22 @@ func (p posToAddedLine) Get(path string, lnum int) *addedLine {
 	return diffline
 }
 
-// addedDiffLines traverse []*diff.FileDiff and returns posToAddedLine.
-func addedDiffLines(filediffs []*diff.FileDiff, strip int) posToAddedLine {
-	r := make(posToAddedLine)
+type lineComparator func(diff.Line) bool
+
+func isAddedLine(line diff.Line) bool {
+	return line.Type == diff.LineAdded
+}
+
+func anyLine(line diff.Line) bool {
+	return true
+}
+
+// significantDiffLines traverse []*diff.FileDiff and returns posToSignificantLine.
+func significantDiffLines(filediffs []*diff.FileDiff, isSignificantLine lineComparator, strip int) posToSignificantLine {
+	r := make(posToSignificantLine)
 	for _, filediff := range filediffs {
 		path := filediff.PathNew
-		ltodiff := make(map[int]*addedLine)
+		ltodiff := make(map[int]*significantLine)
 		if strip > 0 {
 			ps := strings.Split(filepath.ToSlash(filediff.PathNew), "/")
 			if len(ps) > strip {
@@ -102,8 +157,8 @@ func addedDiffLines(filediffs []*diff.FileDiff, strip int) posToAddedLine {
 
 		for _, hunk := range filediff.Hunks {
 			for _, line := range hunk.Lines {
-				if line.Type == diff.LineAdded {
-					ltodiff[line.LnumNew] = &addedLine{
+				if isSignificantLine(*line) {
+					ltodiff[line.LnumNew] = &significantLine{
 						Path:     path,
 						Lnum:     line.LnumNew,
 						LnumDiff: line.LnumDiff,
diff --git a/go.mod b/go.mod
index ebf90aec..0f0074ee 100644
--- a/go.mod
+++ b/go.mod
@@ -5,18 +5,18 @@ go 1.13
 require (
 	cloud.google.com/go v0.51.0
 	cloud.google.com/go/datastore v1.0.0
-	github.com/bradleyfalzon/ghinstallation v1.1.1
+	github.com/bradleyfalzon/ghinstallation v1.1.0
 	github.com/google/go-cmp v0.4.0
 	github.com/google/go-github/v29 v29.0.2
 	github.com/haya14busa/go-actions-toolkit v0.0.0-20200105081403-ca0307860f01
 	github.com/haya14busa/secretbox v0.0.0-20180525171038-07c7ecf409f5
 	github.com/justinas/nosurf v1.1.0
 	github.com/kylelemons/godebug v1.1.0
-	github.com/mattn/go-shellwords v1.0.9
+	github.com/mattn/go-shellwords v1.0.6
 	github.com/rakyll/statik v0.1.6
 	github.com/reviewdog/errorformat v0.0.0-20200109134752-8983be9bc7dd
-	github.com/xanzy/go-gitlab v0.22.3
-	golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa
+	github.com/xanzy/go-gitlab v0.22.2
+	golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 	golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
 	gopkg.in/yaml.v2 v2.2.7
diff --git a/go.sum b/go.sum
index 51ce67f5..a973c487 100644
--- a/go.sum
+++ b/go.sum
@@ -22,8 +22,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/bradleyfalzon/ghinstallation v1.1.0 h1:mwazVinJU0mPyLxIcdtJzu4DhWXFO5lMsWhKyFRIwFk=
 github.com/bradleyfalzon/ghinstallation v1.1.0/go.mod h1:p7iD8KytOOKg2wCqbwvJlq4JGpYMjwjkiqdyUqOIHLI=
-github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
-github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@@ -97,10 +95,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.7 h1:KqhVjVZomx2puPACkj9vrGFqnp42Htvo9SEAWePHKOs=
-github.com/mattn/go-shellwords v1.0.7/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.9 h1:eaB5JspOwiKKcHdqcjbfe5lA9cNn/4NRRtddXJCimqk=
-github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -114,8 +108,6 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/xanzy/go-gitlab v0.22.2 h1:KYPewSm3Tl7WHrVON7BOwX6FZ1gaiFEdpOt0DNIYySA=
 github.com/xanzy/go-gitlab v0.22.2/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og=
-github.com/xanzy/go-gitlab v0.22.3 h1:/rNlZ2hquUWNc6rJdntVM03tEOoTmnZ1lcNyJCl0WlU=
-github.com/xanzy/go-gitlab v0.22.3/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -163,8 +155,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
diff --git a/project/run.go b/project/run.go
index 1610cd92..e6cc81de 100644
--- a/project/run.go
+++ b/project/run.go
@@ -78,7 +78,7 @@ func RunAndParse(ctx context.Context, conf *Config, runners map[string]bool, def
 }
 
 // Run runs reviewdog tasks based on Config.
-func Run(ctx context.Context, conf *Config, runners map[string]bool, c reviewdog.CommentService, d reviewdog.DiffService, teeMode bool) error {
+func Run(ctx context.Context, conf *Config, runners map[string]bool, c reviewdog.CommentService, d reviewdog.DiffService, teeMode bool, filterMode reviewdog.FilterMode) error {
 	results, err := RunAndParse(ctx, conf, runners, "", teeMode) // Level is not used.
 	if err != nil {
 		return err
@@ -98,7 +98,7 @@ func Run(ctx context.Context, conf *Config, runners map[string]bool, c reviewdog
 	results.Range(func(toolname string, result *reviewdog.Result) {
 		rs := result.CheckResults
 		g.Go(func() error {
-			return reviewdog.RunFromResult(ctx, c, rs, filediffs, d.Strip(), toolname)
+			return reviewdog.RunFromResult(ctx, c, rs, filediffs, d.Strip(), toolname, filterMode)
 		})
 	})
 	return g.Wait()
diff --git a/reviewdog.go b/reviewdog.go
index 51542eab..fbcae0ff 100644
--- a/reviewdog.go
+++ b/reviewdog.go
@@ -14,20 +14,22 @@ import (
 // or linter, get diff and filter the results by diff, and report filtered
 // results.
 type Reviewdog struct {
-	toolname string
-	p        Parser
-	c        CommentService
-	d        DiffService
+	toolname   string
+	p          Parser
+	c          CommentService
+	d          DiffService
+	filterMode FilterMode
 }
 
 // NewReviewdog returns a new Reviewdog.
-func NewReviewdog(toolname string, p Parser, c CommentService, d DiffService) *Reviewdog {
-	return &Reviewdog{p: p, c: c, d: d, toolname: toolname}
+func NewReviewdog(toolname string, p Parser, c CommentService, d DiffService, filterMode FilterMode) *Reviewdog {
+	return &Reviewdog{p: p, c: c, d: d, toolname: toolname, filterMode: filterMode}
 }
 
+// RunFromResult creates a new Reviewdog and runs it with check results.
 func RunFromResult(ctx context.Context, c CommentService, results []*CheckResult,
-	filediffs []*diff.FileDiff, strip int, toolname string) error {
-	return (&Reviewdog{c: c, toolname: toolname}).runFromResult(ctx, results, filediffs, strip)
+	filediffs []*diff.FileDiff, strip int, toolname string, filterMode FilterMode) error {
+	return (&Reviewdog{c: c, toolname: toolname, filterMode: filterMode}).runFromResult(ctx, results, filediffs, strip)
 }
 
 // CheckResult represents a checked result of static analysis tools.
@@ -79,7 +81,8 @@ func (w *Reviewdog) runFromResult(ctx context.Context, results []*CheckResult,
 		return err
 	}
 
-	checks := FilterCheck(results, filediffs, strip, wd)
+	checks := FilterCheck(results, filediffs, strip, wd, w.filterMode)
+
 	for _, check := range checks {
 		if !check.InDiff {
 			continue
diff --git a/service/gitlab/gitlab_mr_discussion.go b/service/gitlab/gitlab_mr_discussion.go
index 5f1e79b7..044cf243 100644
--- a/service/gitlab/gitlab_mr_discussion.go
+++ b/service/gitlab/gitlab_mr_discussion.go
@@ -3,6 +3,8 @@ package gitlab
 import (
 	"context"
 	"fmt"
+	"net/http"
+	"net/url"
 	"path/filepath"
 	"sync"
 
@@ -70,7 +72,7 @@ func (g *GitLabMergeRequestDiscussionCommenter) Flush(ctx context.Context) error
 
 func (g *GitLabMergeRequestDiscussionCommenter) createPostedCommetns() (serviceutil.PostedComments, error) {
 	postedcs := make(serviceutil.PostedComments)
-	discussions, err := listAllMergeRequestDiscussion(g.cli, g.projects, g.pr, &gitlab.ListMergeRequestDiscussionsOptions{PerPage: 100})
+	discussions, err := listAllMergeRequestDiscussion(g.cli, g.projects, g.pr, &ListMergeRequestDiscussionOptions{PerPage: 100})
 	if err != nil {
 		return nil, fmt.Errorf("failed to list all merge request discussions: %v", err)
 	}
@@ -103,36 +105,95 @@ func (g *GitLabMergeRequestDiscussionCommenter) postCommentsForEach(ctx context.
 			continue
 		}
 		eg.Go(func() error {
-			discussion := &gitlab.CreateMergeRequestDiscussionOptions{
-				Body: gitlab.String(serviceutil.CommentBody(comment)),
-				Position: &gitlab.NotePosition{
+			discussion := &GitLabMergeRequestDiscussion{
+				Body: serviceutil.CommentBody(comment),
+				Position: &GitLabMergeRequestDiscussionPosition{
 					StartSHA:     targetBranch.Commit.ID,
 					HeadSHA:      g.sha,
-					BaseSHA:      targetBranch.Commit.ID,
+					BaseSHA:      g.sha,
 					PositionType: "text",
 					NewPath:      comment.Path,
 					NewLine:      comment.Lnum,
 				},
 			}
-			_, _, err := g.cli.Discussions.CreateMergeRequestDiscussion(g.projects, g.pr, discussion)
-			if err != nil {
-				return fmt.Errorf("failed to create merge request discussion: %v", err)
-			}
-			return nil
+			_, err := CreateMergeRequestDiscussion(g.cli, g.projects, g.pr, discussion)
+			return err
 		})
 	}
 	return eg.Wait()
 }
 
-func listAllMergeRequestDiscussion(cli *gitlab.Client, projectID string, mergeRequest int, opts *gitlab.ListMergeRequestDiscussionsOptions) ([]*gitlab.Discussion, error) {
-	discussions, resp, err := cli.Discussions.ListMergeRequestDiscussions(projectID, mergeRequest, opts)
+// GitLabMergeRequestDiscussionPosition represents position of GitLab MergeRequest Discussion.
+type GitLabMergeRequestDiscussionPosition struct {
+	// Required.
+	BaseSHA      string `json:"base_sha,omitempty"`      // Base commit SHA in the source branch
+	StartSHA     string `json:"start_sha,omitempty"`     // SHA referencing commit in target branch
+	HeadSHA      string `json:"head_sha,omitempty"`      // SHA referencing HEAD of this merge request
+	PositionType string `json:"position_type,omitempty"` // Type of the position reference', allowed values: 'text' or 'image'
+
+	// Optional.
+	NewPath string `json:"new_path,omitempty"` // File path after change
+	NewLine int    `json:"new_line,omitempty"` // Line number after change (for 'text' diff notes)
+	OldPath string `json:"old_path,omitempty"` // File path before change
+	OldLine int    `json:"old_line,omitempty"` // Line number before change (for 'text' diff notes)
+}
+
+// GitLabMergeRequestDiscussionList represents response of ListMergeRequestDiscussion API.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/discussions.html#list-project-merge-request-discussions
+type GitLabMergeRequestDiscussionList struct {
+	Notes []*GitLabMergeRequestDiscussion `json:"notes"`
+}
+
+// GitLabMergeRequestDiscussion represents a discussion of MergeRequest.
+type GitLabMergeRequestDiscussion struct {
+	Body     string                                `json:"body"` // The content of a discussion
+	Position *GitLabMergeRequestDiscussionPosition `json:"position"`
+}
+
+// CreateMergeRequestDiscussion creates new discussion on a merge request.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/discussions.html#create-new-merge-request-discussion
+func CreateMergeRequestDiscussion(cli *gitlab.Client, projectID string, mergeRequest int, discussion *GitLabMergeRequestDiscussion) (*gitlab.Response, error) {
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions", url.QueryEscape(projectID), mergeRequest)
+	req, err := cli.NewRequest(http.MethodPost, u, discussion, nil)
+	if err != nil {
+		return nil, err
+	}
+	return cli.Do(req, nil)
+}
+
+// ListMergeRequestDiscussion lists discussion on a merge request.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/discussions.html#list-project-merge-request-discussions
+func ListMergeRequestDiscussion(cli *gitlab.Client, projectID string, mergeRequest int, opts *ListMergeRequestDiscussionOptions) ([]*GitLabMergeRequestDiscussionList, *gitlab.Response, error) {
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/discussions", url.QueryEscape(projectID), mergeRequest)
+	req, err := cli.NewRequest(http.MethodGet, u, opts, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	var discussions []*GitLabMergeRequestDiscussionList
+	resp, err := cli.Do(req, &discussions)
+	if err != nil {
+		return nil, resp, err
+	}
+	return discussions, resp, nil
+}
+
+// ListMergeRequestDiscussionOptions represents the available ListMergeRequestDiscussion() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/discussions.html#list-project-merge-request-discussions
+type ListMergeRequestDiscussionOptions gitlab.ListOptions
+
+func listAllMergeRequestDiscussion(cli *gitlab.Client, projectID string, mergeRequest int, opts *ListMergeRequestDiscussionOptions) ([]*GitLabMergeRequestDiscussionList, error) {
+	discussions, resp, err := ListMergeRequestDiscussion(cli, projectID, mergeRequest, opts)
 	if err != nil {
 		return nil, err
 	}
 	if resp.NextPage == 0 {
 		return discussions, nil
 	}
-	newOpts := &gitlab.ListMergeRequestDiscussionsOptions{
+	newOpts := &ListMergeRequestDiscussionOptions{
 		Page:    resp.NextPage,
 		PerPage: opts.PerPage,
 	}


Test patch:
diff --git a/cmd/reviewdog/doghouse_test.go b/cmd/reviewdog/doghouse_test.go
index 679b7b01..20659aa7 100644
--- a/cmd/reviewdog/doghouse_test.go
+++ b/cmd/reviewdog/doghouse_test.go
@@ -273,7 +273,7 @@ func TestPostResultSet_withReportURL(t *testing.T) {
 		SHA:         sha,
 	}
 
-	if _, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, true); err != nil {
+	if _, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, true, reviewdog.FilterModeAdded); err != nil {
 		t.Fatal(err)
 	}
 }
@@ -297,7 +297,7 @@ func TestPostResultSet_withoutReportURL(t *testing.T) {
 
 	ghInfo := &cienv.BuildInfo{Owner: owner, Repo: repo, PullRequest: prNum, SHA: sha}
 
-	resp, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, true)
+	resp, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, true, reviewdog.FilterModeAdded)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -331,7 +331,7 @@ func TestPostResultSet_withEmptyResponse(t *testing.T) {
 
 	ghInfo := &cienv.BuildInfo{Owner: owner, Repo: repo, PullRequest: prNum, SHA: sha}
 
-	if _, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, true); err == nil {
+	if _, err := postResultSet(context.Background(), &resultSet, ghInfo, fakeCli, true, reviewdog.FilterModeAdded); err == nil {
 		t.Error("got no error but want report missing error")
 	}
 }
diff --git a/filter_test.go b/filter_test.go
index c4507973..5ae9a2c6 100644
--- a/filter_test.go
+++ b/filter_test.go
@@ -34,7 +34,7 @@ const diffContent = `--- sample.old.txt	2016-10-13 05:09:35.820791185 +0900
 \ No newline at end of file
 `
 
-func TestFilterCheck(t *testing.T) {
+func TestFilterCheckByAddedLines(t *testing.T) {
 	results := []*CheckResult{
 		{
 			Path: "sample.new.txt",
@@ -86,7 +86,56 @@ func TestFilterCheck(t *testing.T) {
 		},
 	}
 	filediffs, _ := diff.ParseMultiFile(strings.NewReader(diffContent))
-	got := FilterCheck(results, filediffs, 0, "")
+	got := FilterCheck(results, filediffs, 0, "", FilterModeAdded)
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Error(diff)
+	}
+}
+
+// All lines that are in diff are taken into account
+func TestFilterCheckByDiffContext(t *testing.T) {
+	results := []*CheckResult{
+		{
+			Path: "sample.new.txt",
+			Lnum: 1,
+		},
+		{
+			Path: "sample.new.txt",
+			Lnum: 2,
+		},
+		{
+			Path: "sample.new.txt",
+			Lnum: 3,
+		},
+	}
+	want := []*FilteredCheck{
+		{
+			CheckResult: &CheckResult{
+				Path: "sample.new.txt",
+				Lnum: 1,
+			},
+			InDiff:   true,
+			LnumDiff: 1,
+		},
+		{
+			CheckResult: &CheckResult{
+				Path: "sample.new.txt",
+				Lnum: 2,
+			},
+			InDiff:   true,
+			LnumDiff: 3,
+		},
+		{
+			CheckResult: &CheckResult{
+				Path: "sample.new.txt",
+				Lnum: 3,
+			},
+			InDiff:   true,
+			LnumDiff: 4,
+		},
+	}
+	filediffs, _ := diff.ParseMultiFile(strings.NewReader(diffContent))
+	got := FilterCheck(results, filediffs, 0, "", FilterModeDiffContext)
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Error(diff)
 	}
@@ -102,7 +151,7 @@ func TestAddedDiffLines(t *testing.T) {
 		"nonewline.new.txt:4:(difflnum:6) b",
 	}
 	var gotlines []string
-	for path, ltol := range addedDiffLines(filediffs, 0) {
+	for path, ltol := range significantDiffLines(filediffs, isAddedLine, 0) {
 		for lnum, addedline := range ltol {
 			l := fmt.Sprintf("%v:%v:(difflnum:%v) %v", path[len(wd)+1:], lnum, addedline.LnumDiff, addedline.Content)
 			gotlines = append(gotlines, l)
diff --git a/project/run_test.go b/project/run_test.go
index ba72c3e0..7140d37e 100644
--- a/project/run_test.go
+++ b/project/run_test.go
@@ -38,7 +38,7 @@ func TestRun(t *testing.T) {
 
 	t.Run("empty", func(t *testing.T) {
 		conf := &Config{}
-		if err := Run(ctx, conf, nil, nil, nil, false); err != nil {
+		if err := Run(ctx, conf, nil, nil, nil, false, reviewdog.FilterModeAdded); err != nil {
 			t.Error(err)
 		}
 	})
@@ -49,7 +49,7 @@ func TestRun(t *testing.T) {
 				"test": {},
 			},
 		}
-		if err := Run(ctx, conf, nil, nil, nil, false); err == nil {
+		if err := Run(ctx, conf, nil, nil, nil, false, reviewdog.FilterModeAdded); err == nil {
 			t.Error("want error, got nil")
 		} else {
 			t.Log(err)
@@ -70,7 +70,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, nil, nil, ds, false); err == nil {
+		if err := Run(ctx, conf, nil, nil, ds, false, reviewdog.FilterModeAdded); err == nil {
 			t.Error("want error, got nil")
 		} else {
 			t.Log(err)
@@ -98,7 +98,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, nil, cs, ds, false); err != nil {
+		if err := Run(ctx, conf, nil, cs, ds, false, reviewdog.FilterModeAdded); err != nil {
 			t.Error(err)
 		}
 		want := ""
@@ -128,7 +128,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, nil, cs, ds, true); err != nil {
+		if err := Run(ctx, conf, nil, cs, ds, true, reviewdog.FilterModeAdded); err != nil {
 			t.Error(err)
 		}
 		want := "sh: 1: not: not found\n"
@@ -156,7 +156,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, nil, cs, ds, false); err != nil {
+		if err := Run(ctx, conf, nil, cs, ds, false, reviewdog.FilterModeAdded); err != nil {
 			t.Error(err)
 		}
 	})
@@ -182,7 +182,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, nil, cs, ds, true); err != nil {
+		if err := Run(ctx, conf, nil, cs, ds, true, reviewdog.FilterModeAdded); err != nil {
 			t.Error(err)
 		}
 		want := "hi\n"
@@ -218,7 +218,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, map[string]bool{"test2": true}, cs, ds, false); err != nil {
+		if err := Run(ctx, conf, map[string]bool{"test2": true}, cs, ds, false, reviewdog.FilterModeAdded); err != nil {
 			t.Error(err)
 		}
 		if called != 1 {
@@ -251,7 +251,7 @@ func TestRun(t *testing.T) {
 				},
 			},
 		}
-		if err := Run(ctx, conf, map[string]bool{"hoge": true}, cs, ds, false); err == nil {
+		if err := Run(ctx, conf, map[string]bool{"hoge": true}, cs, ds, false, reviewdog.FilterModeAdded); err == nil {
 			t.Error("got no error but want runner not found error")
 		}
 	})
diff --git a/reviewdog_test.go b/reviewdog_test.go
index 200f1fe8..6d7bf81c 100644
--- a/reviewdog_test.go
+++ b/reviewdog_test.go
@@ -47,7 +47,7 @@ golint.new.go:11:1: comment on exported function F2 should be of the form "F2 ..
 	p := NewErrorformatParser(efm)
 	c := NewRawCommentWriter(os.Stdout)
 	d := NewDiffString(difftext, 1)
-	app := NewReviewdog("tool name", p, c, d)
+	app := NewReviewdog("tool name", p, c, d, FilterModeAdded)
 	app.Run(context.Background(), strings.NewReader(lintresult))
 	// Unordered output:
 	// golint.new.go:5:5: exported var NewError1 should have comment or be unexported
@@ -93,6 +93,6 @@ index 34cacb9..a727dd3 100644
 	efm, _ := errorformat.NewErrorformat([]string{`%f:%l:%c: %m`})
 	p := NewErrorformatParser(efm)
 	d := NewDiffString(difftext, 1)
-	app := NewReviewdog("tool name", p, c, d)
+	app := NewReviewdog("tool name", p, c, d, FilterModeAdded)
 	app.Run(context.Background(), strings.NewReader(lintresult))
 }
diff --git a/service/gitlab/gitlab_mr_discussion_test.go b/service/gitlab/gitlab_mr_discussion_test.go
index b2942c13..f2ac99b7 100644
--- a/service/gitlab/gitlab_mr_discussion_test.go
+++ b/service/gitlab/gitlab_mr_discussion_test.go
@@ -62,19 +62,19 @@ func TestGitLabMergeRequestDiscussionCommenter_Post_Flush_review_api(t *testing.
 		case http.MethodGet:
 			switch r.URL.Query().Get("page") {
 			default:
-				dls := []*gitlab.Discussion{
+				dls := []*GitLabMergeRequestDiscussionList{
 					{
-						Notes: []*gitlab.Note{
+						Notes: []*GitLabMergeRequestDiscussion{
 							{
 								Body: serviceutil.CommentBody(alreadyCommented1),
-								Position: &gitlab.NotePosition{
+								Position: &GitLabMergeRequestDiscussionPosition{
 									NewPath: alreadyCommented1.Path,
 									NewLine: alreadyCommented1.Lnum,
 								},
 							},
 							{
 								Body: "unrelated commented",
-								Position: &gitlab.NotePosition{
+								Position: &GitLabMergeRequestDiscussionPosition{
 									NewPath: "file.go",
 									NewLine: 1,
 								},
@@ -87,12 +87,12 @@ func TestGitLabMergeRequestDiscussionCommenter_Post_Flush_review_api(t *testing.
 					t.Fatal(err)
 				}
 			case "2":
-				dls := []*gitlab.Discussion{
+				dls := []*GitLabMergeRequestDiscussionList{
 					{
-						Notes: []*gitlab.Note{
+						Notes: []*GitLabMergeRequestDiscussion{
 							{
 								Body: serviceutil.CommentBody(alreadyCommented2),
-								Position: &gitlab.NotePosition{
+								Position: &GitLabMergeRequestDiscussionPosition{
 									NewPath: alreadyCommented2.Path,
 									NewLine: alreadyCommented2.Lnum,
 								},
@@ -106,25 +106,25 @@ func TestGitLabMergeRequestDiscussionCommenter_Post_Flush_review_api(t *testing.
 			}
 
 		case http.MethodPost:
-			got := new(gitlab.CreateMergeRequestDiscussionOptions)
+			got := new(GitLabMergeRequestDiscussion)
 			if err := json.NewDecoder(r.Body).Decode(got); err != nil {
 				t.Error(err)
 			}
 			switch got.Position.NewPath {
 			case "file.go":
-				want := &gitlab.CreateMergeRequestDiscussionOptions{
-					Body: gitlab.String(serviceutil.CommentBody(newComment1)),
-					Position: &gitlab.NotePosition{
-						BaseSHA: "xxx", StartSHA: "xxx", HeadSHA: "sha", PositionType: "text", NewPath: "file.go", NewLine: 14},
+				want := &GitLabMergeRequestDiscussion{
+					Body: serviceutil.CommentBody(newComment1),
+					Position: &GitLabMergeRequestDiscussionPosition{
+						BaseSHA: "sha", StartSHA: "xxx", HeadSHA: "sha", PositionType: "text", NewPath: "file.go", NewLine: 14},
 				}
 				if diff := cmp.Diff(got, want); diff != "" {
 					t.Error(diff)
 				}
 			case "file2.go":
-				want := &gitlab.CreateMergeRequestDiscussionOptions{
-					Body: gitlab.String(serviceutil.CommentBody(newComment2)),
-					Position: &gitlab.NotePosition{
-						BaseSHA: "xxx", StartSHA: "xxx", HeadSHA: "sha", PositionType: "text", NewPath: "file2.go", NewLine: 15},
+				want := &GitLabMergeRequestDiscussion{
+					Body: serviceutil.CommentBody(newComment2),
+					Position: &GitLabMergeRequestDiscussionPosition{
+						BaseSHA: "sha", StartSHA: "xxx", HeadSHA: "sha", PositionType: "text", NewPath: "file2.go", NewLine: 15},
 				}
 				if diff := cmp.Diff(got, want); diff != "" {
 					t.Error(diff)
@@ -132,9 +132,6 @@ func TestGitLabMergeRequestDiscussionCommenter_Post_Flush_review_api(t *testing.
 			default:
 				t.Errorf("got unexpected discussion: %#v", got)
 			}
-			if err := json.NewEncoder(w).Encode(gitlab.Discussion{}); err != nil {
-				t.Fatal(err)
-			}
 		default:
 			t.Errorf("unexpected access: %v %v", r.Method, r.URL)
 		}
@@ -169,6 +166,6 @@ func TestGitLabMergeRequestDiscussionCommenter_Post_Flush_review_api(t *testing.
 		}
 	}
 	if err := g.Flush(context.Background()); err != nil {
-		t.Errorf("%v", err)
+		t.Error(err)
 	}
 }