docs: function watchchangeInit() {
  
      var listenners = {}, changedFile = {};
  
      var onChange = grunt.util._.debounce(function() {
        grunt.log.writeln('Changed ' +
          grunt.util.pluralize(changedFile.length, 'file /files ') +
          grunt.log.wordlist(Object.keys(changedFile)));
  
        grunt.event.emit('changed', changedFile);
        changedFile = {};
      }, 200);
  
      watchchangeTask = function watchchangeTask() {
        var self = this;
        var name = self.name;
  
        // In case user renames our task,
        //   we register watch listenner once for each name.
        if (!listenners[name]) {
          listenners[name] = {};
          grunt.event.on('watch', function(action, filepath, target) {
            changedFile[filepath] = {
              action: action,
              filepath: filepath,
              target: target
            };
  
            onChange();
          });
        }
  
        var options = this.options({
          replace: undefined,
          replaceBy: undefined,
          match: undefined,
          action: undefined,
          destConfig: [],
          tasks: [],
          prefilter: undefined,
          postfilter: undefined,
          preprocess: undefined,
          postprocess: undefined,
          timeout: 0
        });
        var data = grunt.util._.merge(options, this.data);
  
        var actions = {};
        if (typeof data.action == 'object') {
          for (var i in data.action) {
            action[data.action[i]] = true;
          }
  
        } else if (typeof data.action == 'string') {
          actions[data.action] = true;
        }
  
        // In case user calls our task many times, we only keep one listenner.
        var listenner = listenners[name][self.target] ?
          grunt.event.off('changed', listenner) : 0;
  
        grunt.event.on('changed', function(changed) {
          var i;
  
          var filenames = Object.keys(changed);
          filenames = !data.match ? filenames :
            grunt.file.match(processTemplate(data.match), filenames);
  
          var files = filenames.map(function(i) {
            return changed[i];
          });
  
          files = !data.action ? files : grunt.file.filter(function(f) {
            return actions[f.action];
          });
  
          files = !data.preprocess ? files :
            files.map(function(f) {
              return {
                filepath: data.preprocess(f.filepath, f.action, f.target),
                action: f.action,
                target: f.target
              };
            });
  
          files = !data.prefilter ? files :
            files.filter(function(f) {
              return data.prefilter(f.filepath, f.action, f.target);
            });
  
          files = !data.replace ? files :
            files.map(function(f) {
              return {
                filepath: f.filepath.replace(data.replace, data.replaceBy),
                action: f.action,
                target: f.target
              };
            });
  
          files = !data.postprocess ? files :
            files.map(function(f) {
              return {
                filepath: data.postprocess(f.filepath, f.action, f.target),
                action: f.action,
                target: f.target
              };
            });
  
          files = !data.postfilter ? files :
            files.filter(function(f) {
              return data.postfilter(f.filepath, f.action, f.target);
            });
  
          processDestFiles(self, files.map(function(f) {
            return f.filepath;
          }), options.timeout);
  
        });
      };
    }