From 023676eda2006a9ce20975907a69ce8afec0dece Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Tue, 28 Jul 2015 00:13:36 -0400 Subject: [PATCH] Add pre-commit hook for hlint --- config/default.yml | 7 +++ lib/overcommit/hook/pre_commit/hlint.rb | 32 ++++++++++ spec/overcommit/hook/pre_commit/hlint_spec.rb | 58 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 lib/overcommit/hook/pre_commit/hlint.rb create mode 100644 spec/overcommit/hook/pre_commit/hlint_spec.rb diff --git a/config/default.yml b/config/default.yml index 1dfccef5..ed409705 100644 --- a/config/default.yml +++ b/config/default.yml @@ -225,6 +225,13 @@ PreCommit: required_executable: 'grep' flags: ['-IHn', "\t"] + Hlint: + enabled: false + description: 'Analyzing with hlint' + required_executable: 'hlint' + install_command: 'cabal install hlint' + include: '**/*.hs' + HtmlHint: enabled: false description: 'Analyzing with HTMLHint' diff --git a/lib/overcommit/hook/pre_commit/hlint.rb b/lib/overcommit/hook/pre_commit/hlint.rb new file mode 100644 index 00000000..fcbfa122 --- /dev/null +++ b/lib/overcommit/hook/pre_commit/hlint.rb @@ -0,0 +1,32 @@ +module Overcommit::Hook::PreCommit + # Runs `hlint` against any modified Haskell files. + # + # @see https://github.com/ndmitchell/hlint + class Hlint < Base + MESSAGE_REGEX = / + ^(?(?:\w:)?[^:]+) + :(?\d+) + :\d+ + :\s*(?\w+) + /x + + MESSAGE_TYPE_CATEGORIZER = lambda do |type| + type.include?('W') ? :warning : :error + end + + def run + result = execute(command, args: applicable_files) + return :pass if result.success? + + raw_messages = result.stdout.split("\n").grep(MESSAGE_REGEX) + + # example message: + # path/to/file.hs:1:0: Error: message + extract_messages( + raw_messages, + MESSAGE_REGEX, + MESSAGE_TYPE_CATEGORIZER + ) + end + end +end diff --git a/spec/overcommit/hook/pre_commit/hlint_spec.rb b/spec/overcommit/hook/pre_commit/hlint_spec.rb new file mode 100644 index 00000000..08183e18 --- /dev/null +++ b/spec/overcommit/hook/pre_commit/hlint_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe Overcommit::Hook::PreCommit::Hlint do + let(:config) { Overcommit::ConfigurationLoader.default_configuration } + let(:context) { double('context') } + subject { described_class.new(config, context) } + + let(:result) { double('result') } + + before do + subject.stub(:applicable_files).and_return(%w[file1.hs file2.hs]) + subject.stub(:execute).and_return(result) + end + + context 'when hlint exits successfully' do + before do + result.stub(success?: true, stdout: '') + end + + it { should pass } + end + + context 'when hlint exits unsucessfully' do + let(:result) { double('result') } + + before do + result.stub(:success?).and_return(false) + end + + context 'and it reports a warning' do + before do + result.stub(:stdout).and_return(normalize_indent(<<-OUT)) + file1.hs:22:16: Warning: Use const + Found: + \\ _ -> False + Why not: + const False + OUT + end + + it { should warn } + end + + context 'and it reports an error' do + before do + result.stub(:stdout).and_return(normalize_indent(<<-OUT)) + file1.hs:22:5: Error: Redundant lambda + Found: + nameHack = \\ _ -> Nothing + Why not: + nameHack _ = Nothing + OUT + end + + it { should fail_hook } + end + end +end