[med-svn] [Git][med-team/seqkit][master] 7 commits: d/watch: Fix watch regex

Nilesh Patra gitlab at salsa.debian.org
Sat May 1 19:36:14 BST 2021



Nilesh Patra pushed to branch master at Debian Med / seqkit


Commits:
76438fe8 by Nilesh Patra at 2021-05-01T22:35:34+05:30
d/watch: Fix watch regex

- - - - -
e77f29a7 by Nilesh Patra at 2021-05-01T22:40:17+05:30
New upstream version 0.16.0+ds
- - - - -
19e764a7 by Nilesh Patra at 2021-05-01T22:40:48+05:30
Update upstream source from tag 'upstream/0.16.0+ds'

Update to upstream version '0.16.0+ds'
with Debian dir a53f5df6b22c552746abb06d4afd3fdbc90d5fff
- - - - -
fa1847f1 by Nilesh Patra at 2021-05-01T22:44:18+05:30
d/p/spellings.patch: Refresh patch

- - - - -
59ba82bf by Nilesh Patra at 2021-05-01T22:55:19+05:30
Add B-D on golang-github-twotwotwo-sorts-dev

- - - - -
901256d9 by Nilesh Patra at 2021-05-01T23:55:29+05:30
Tighten B-D on golang-github-shenwei356-bio-dev and golang-github-shenwei356-bwt-dev

- - - - -
35416836 by Nilesh Patra at 2021-05-01T23:59:24+05:30
Add TODO

- - - - -


21 changed files:

- CHANGES.md → CHANGELOG.md
- README.md
- debian/changelog
- debian/control
- debian/patches/spellings.patch
- debian/watch
- doc/docs/download.md
- doc/docs/usage.md
- doc/mkdocs.yml
- go.mod
- go.sum
- seqkit/cmd/amplicon.go
- seqkit/cmd/bam_toolbox.go
- seqkit/cmd/bed.go
- seqkit/cmd/fx2tab.go
- seqkit/cmd/genautocomplete.go
- seqkit/cmd/grep.go
- + seqkit/cmd/head-genome.go
- seqkit/cmd/locate.go
- seqkit/cmd/tab2fx.go
- seqkit/cmd/version.go


Changes:

=====================================
CHANGES.md → CHANGELOG.md
=====================================
@@ -1,3 +1,27 @@
+
+
+- [SeqKit v0.16.0](https://github.com/shenwei356/seqkit/releases/tag/v0.16.0)
+[![Github Releases (by Release)](https://img.shields.io/github/downloads/shenwei356/seqkit/v0.16.0/total.svg)](https://github.com/shenwei356/seqkit/releases/tag/v0.16.0)
+    - new command `seqkit head-genome`:
+        - print sequences of the first genome with common prefixes in name
+    - `seqkit grep/locate/amplicon -m`
+        - *much faster (300-400x) searching with mismatch allowed* by optimizing FM-indexing and parallelization.
+        - new flag `-I/--immediate-output`.
+    - `seqkit grep/locate`:
+        - fix bug of `-m` when querying contains letters not in alphabet, usually for protein sequences. [#178](https://github.com/shenwei356/seqkit/issues/178), [#179](https://github.com/shenwei356/seqkit/issues/179)
+        - onply search on positive strand when searching unlimited or protein sequences.
+    - `seqkit locate`:
+        - removing debug info for `-r` introduced in a0f6b6e. [#180](https://github.com/shenwei356/seqkit/issues/180)
+    - `seqkit amplicon`:
+        - fix bug of `-m`, when mismatch is allowed.
+    - `seqkit fx2tab`:
+        - new flag `-C/--base-count` for counting bases. [#183](https://github.com/shenwei356/seqkit/issues/183)
+    -  `seqkit tab2fx`:
+        -  fix a rare bug. [#197](https://github.com/shenwei356/seqkit/issues/197)
+    - `seqkit subseq`:
+        - fix bug for BED with empty columns. [#195](https://github.com/shenwei356/seqkit/issues/195)
+    - `seqkit genautocomplete`: 
+        - **support bash|zsh|fish|powershell**.
 - [SeqKit v0.15.0](https://github.com/shenwei356/seqkit/releases/tag/v0.15.0)
 [![Github Releases (by Release)](https://img.shields.io/github/downloads/shenwei356/seqkit/v0.15.0/total.svg)](https://github.com/shenwei356/seqkit/releases/tag/v0.15.0)
     - `seqkit grep/locate`: update help message.


=====================================
README.md
=====================================
@@ -19,6 +19,7 @@ and
 - [![Gitter](https://badges.gitter.im/seqkit/community.svg)](https://gitter.im/seqkit/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
 
 
+
 ## Introduction
 
 FASTA and FASTQ are basic and ubiquitous formats for storing nucleotide and
@@ -47,7 +48,7 @@ enable researchers to rapidly accomplish common FASTA/Q file manipulations.
 - [Features](#features)
 - [Subcommands](#subcommands)
 - [Installation](#installation)
-- [Bash-completion](#bash-completion)
+- [Command-line completion](#command-line-completion)
 - [Technical details and guides for use](#technical-details-and-guides-for-use)
 - [Usage && Examples](#usage--examples)
 - [Benchmark](#benchmark)
@@ -118,7 +119,7 @@ enable researchers to rapidly accomplish common FASTA/Q file manipulations.
 
 ## Subcommands
 
-34 functional subcommands in total.
+35 functional subcommands in total.
 
 **Sequence and subsequence**
 
@@ -153,6 +154,7 @@ enable researchers to rapidly accomplish common FASTA/Q file manipulations.
 **Set operations**
 
 - [`head`](https://bioinf.shenwei.me/seqkit/usage/#head)            print first N FASTA/Q records
+- [`head-genome`](https://bioinf.shenwei.me/seqkit/usage/#head-genome) print sequences of the first genome with common prefixes in name
 - [`range`](https://bioinf.shenwei.me/seqkit/usage/#range)          print FASTA/Q records in a range (start:end)
 - [`sample`](https://bioinf.shenwei.me/seqkit/usage/#sample)        sample sequences by number or proportion
 - [`rmdup`](https://bioinf.shenwei.me/seqkit/usage/#rmdup)          remove duplicated sequences by id/name/sequence
@@ -178,7 +180,7 @@ enable researchers to rapidly accomplish common FASTA/Q file manipulations.
 **Misc**
 
 - `version`   print version information and check for update
-- `genautocomplete` generate shell autocompletion script
+- `genautocomplete` generate shell autocompletion script (bash|zsh|fish|powershell)
 
 ## Installation
 
@@ -234,24 +236,33 @@ Run the following commands:
     docker run -it shenwei356/seqkit:latest
 
 
-## Bash-completion
+## Command-line completion
+
+Supported shell: bash|zsh|fish|powershell
+
+Bash:
+
+    # generate completion shell
+    seqkit genautocomplete --shell bash
 
-Note: The current version supports Bash only.
-This should work for *nix systems with Bash installed.
+    # configure if never did.
+    # install bash-completion if the "complete" command is not found.
+    echo "for bcfile in ~/.bash_completion.d/* ; do source \$bcfile; done" >> ~/.bash_completion
+    echo "source ~/.bash_completion" >> ~/.bashrc
 
-Howto:
+Zsh:
 
-1. run: `seqkit genautocomplete`
+    # generate completion shell
+    seqkit genautocomplete --shell zsh --file ~/.zfunc/_seqkit
 
-2. create and edit `~/.bash_completion` file if you don't have it.
+    # configure if never did
+    echo 'fpath=( ~/.zfunc "${fpath[@]}" )' >> ~/.zshrc
+    echo "autoload -U compinit; compinit" >> ~/.zshrc
 
-        nano ~/.bash_completion
+fish:
 
-    add the following:
+    seqkit genautocomplete --shell fish --file ~/.config/fish/completions/seqkit.fish
 
-        for bcfile in ~/.bash_completion.d/* ; do
-          . $bcfile
-        done
 
 ## Technical details and guides for use
 


=====================================
debian/changelog
=====================================
@@ -1,3 +1,18 @@
+seqkit (0.16.0+ds-1) UNRELEASED; urgency=medium
+
+  * d/watch: Fix watch regex
+  * New upstream version 0.16.0+ds
+  * d/p/spellings.patch: Refresh patch
+  * Add B-D on golang-github-twotwotwo-sorts-dev
+  * Tighten B-D on golang-github-shenwei356-bio-dev
+    and golang-github-shenwei356-bwt-dev
+ 
+  TODO: Upload new upstream release of golang-github-shenwei356-breader,
+        golang-github-shenwei356-bio, golang-github-shenwei356-bwt, golang-github-shenwei356-util
+        before uploading this. The changes to the all are in salsa.
+
+ -- Nilesh Patra <nilesh at debian.org>  Sat, 01 May 2021 23:56:08 +0530
+
 seqkit (0.15.0+ds-2) unstable; urgency=medium
 
   * d/watch: do not rank rc versions higher than real releases


=====================================
debian/control
=====================================
@@ -19,12 +19,13 @@ Build-Depends: debhelper-compat (= 13),
                golang-github-mattn-go-isatty-dev,
                golang-github-mitchellh-go-homedir-dev,
                golang-github-op-go-logging-dev,
-               golang-github-shenwei356-bio-dev,
-               golang-github-shenwei356-bwt-dev,
+               golang-github-shenwei356-bio-dev (>= 0.1.0~),
+               golang-github-shenwei356-bwt-dev (>= 0.5.1~),
                golang-github-spf13-cobra-dev,
                golang-github-tatsushid-go-prettytable-dev,
                golang-github-iafan-cwalk-dev,
                golang-github-smallfish-simpleyaml-dev,
+               golang-github-twotwotwo-sorts-dev,
                samtools
 Standards-Version: 4.5.1
 Vcs-Browser: https://salsa.debian.org/med-team/seqkit


=====================================
debian/patches/spellings.patch
=====================================
@@ -168,15 +168,15 @@ Last Changed: August 3, 2020
  }
 --- a/seqkit/cmd/amplicon.go
 +++ b/seqkit/cmd/amplicon.go
-@@ -50,7 +50,7 @@
+@@ -52,7 +52,7 @@
  
  Attentions:
    1. Only one (the longest) matching location is returned for every primer pair.
 -  2. Mismatch is allowed, but the mismatch location (5' or 3') is not controled. 
 +  2. Mismatch is allowed, but the mismatch location (5' or 3') is not controlled.
+      You can increase the value of "-j/--threads" to accelerate processing.
    3. Degenerate bases/residues like "RYMM.." are also supported.
       But do not use degenerate bases/residues in regular expression, you need
-      convert them to regular expression, e.g., change "N" or "X"  to ".".
 --- a/seqkit/cmd/scat.go
 +++ b/seqkit/cmd/scat.go
 @@ -566,7 +566,7 @@


=====================================
debian/watch
=====================================
@@ -1,4 +1,4 @@
 version=4
 
 opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%@PACKAGE at -$1.tar.gz%,uversionmangle=s/rc/~rc/,repacksuffix=+ds,dversionmangle=auto" \
-https://github.com/shenwei356/seqkit/releases .*/archive/v?@ANY_VERSION@\.tar\.gz
+https://github.com/shenwei356/seqkit/releases .*/archive/.*/v?@ANY_VERSION@\.tar\.gz


=====================================
doc/docs/download.md
=====================================
@@ -6,14 +6,28 @@ SeqKit is implemented in [Go](https://golang.org/) programming language,
 
 ## Latest Version
 
-- [SeqKit v0.15.0](https://github.com/shenwei356/seqkit/releases/tag/v0.15.0)
-[![Github Releases (by Release)](https://img.shields.io/github/downloads/shenwei356/seqkit/v0.15.0/total.svg)](https://github.com/shenwei356/seqkit/releases/tag/v0.15.0)
-    - `seqkit grep/locate`: update help message.
-    - `seqkit grep`: **search on both strand when searching by sequence**.
-    - `seqkit split2`: fix redundant log when using `-s`.
-    - `seqkit bam`: new field `RightSoftClipSeq`. [#172](https://github.com/shenwei356/seqkit/pull/172)
-    - `seqkit sample -2`: remove extra `\n`. [#173](https://github.com/shenwei356/seqkit/issues/173)
-    - `seqkit split2 -l`: fix bug for splitting by accumulative length, this bug occurs when the first record is longer than `-l`, no sequences are lost.
+- [SeqKit v0.16.0](https://github.com/shenwei356/seqkit/releases/tag/v0.16.0)
+[![Github Releases (by Release)](https://img.shields.io/github/downloads/shenwei356/seqkit/v0.16.0/total.svg)](https://github.com/shenwei356/seqkit/releases/tag/v0.16.0)
+    - new command `seqkit head-genome`:
+        - print sequences of the first genome with common prefixes in name
+    - `seqkit grep/locate/amplicon -m`
+        - *much faster (300-400x) searching with mismatch allowed* by optimizing FM-indexing and parallelization.
+        - new flag `-I/--immediate-output`.
+    - `seqkit grep/locate`:
+        - fix bug of `-m` when querying contains letters not in alphabet, usually for protein sequences. [#178](https://github.com/shenwei356/seqkit/issues/178), [#179](https://github.com/shenwei356/seqkit/issues/179)
+        - onply search on positive strand when searching unlimited or protein sequences.
+    - `seqkit locate`:
+        - removing debug info for `-r` introduced in a0f6b6e. [#180](https://github.com/shenwei356/seqkit/issues/180)
+    - `seqkit amplicon`:
+        - fix bug of `-m`, when mismatch is allowed.
+    - `seqkit fx2tab`:
+        - new flag `-C/--base-count` for counting bases. [#183](https://github.com/shenwei356/seqkit/issues/183)
+    -  `seqkit tab2fx`:
+        -  fix a rare bug. [#197](https://github.com/shenwei356/seqkit/issues/197)
+    - `seqkit subseq`:
+        - fix bug for BED with empty columns. [#195](https://github.com/shenwei356/seqkit/issues/195)
+    - `seqkit genautocomplete`: 
+        - **support bash|zsh|fish|powershell**.
 
 ### Please cite
 
@@ -30,13 +44,13 @@ SeqKit is implemented in [Go](https://golang.org/) programming language,
 
 OS     |Arch      |File, 中国镜像                                                                                                                                                                                  |Download Count
 :------|:---------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-Linux  |32-bit    |[seqkit_linux_386.tar.gz](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_linux_386.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_linux_386.tar.gz)                            |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_linux_386.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_linux_386.tar.gz)
-Linux  |**64-bit**|[**seqkit_linux_amd64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_linux_amd64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_linux_amd64.tar.gz)                  |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_linux_amd64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_linux_amd64.tar.gz)
-Linux  |**arm64** |[**seqkit_linux_arm64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_linux_arm64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_linux_arm64.tar.gz)                  |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_linux_arm64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_linux_arm64.tar.gz)
-macOS  |**64-bit**|[**seqkit_darwin_amd64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_darwin_amd64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_darwin_amd64.tar.gz)               |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_darwin_amd64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_darwin_amd64.tar.gz)
-macOS  |**arm64** |[**seqkit_darwin_arm64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_darwin_arm64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_darwin_arm64.tar.gz)               |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_darwin_arm64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_darwin_arm64.tar.gz)
-Windows|32-bit    |[seqkit_windows_386.exe.tar.gz](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_windows_386.exe.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_windows_386.exe.tar.gz)          |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_windows_386.exe.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_windows_386.exe.tar.gz)
-Windows|**64-bit**|[**seqkit_windows_amd64.exe.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_windows_amd64.exe.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_windows_amd64.exe.tar.gz)|[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_windows_amd64.exe.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.15.0/seqkit_windows_amd64.exe.tar.gz)
+Linux  |32-bit    |[seqkit_linux_386.tar.gz](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_linux_386.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_linux_386.tar.gz)                            |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_linux_386.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_linux_386.tar.gz)
+Linux  |**64-bit**|[**seqkit_linux_amd64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_linux_amd64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_linux_amd64.tar.gz)                  |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_linux_amd64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_linux_amd64.tar.gz)
+Linux  |**arm64** |[**seqkit_linux_arm64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_linux_arm64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_linux_arm64.tar.gz)                  |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_linux_arm64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_linux_arm64.tar.gz)
+macOS  |**64-bit**|[**seqkit_darwin_amd64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_darwin_amd64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_darwin_amd64.tar.gz)               |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_darwin_amd64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_darwin_amd64.tar.gz)
+macOS  |**arm64** |[**seqkit_darwin_arm64.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_darwin_arm64.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_darwin_arm64.tar.gz)               |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_darwin_arm64.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_darwin_arm64.tar.gz)
+Windows|32-bit    |[seqkit_windows_386.exe.tar.gz](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_windows_386.exe.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_windows_386.exe.tar.gz)          |[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_windows_386.exe.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_windows_386.exe.tar.gz)
+Windows|**64-bit**|[**seqkit_windows_amd64.exe.tar.gz**](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_windows_amd64.exe.tar.gz), <br/> [中国镜像](http://app.shenwei.me/data/seqkit/seqkit_windows_amd64.exe.tar.gz)|[![Github Releases (by Asset)](https://img.shields.io/github/downloads/shenwei356/seqkit/latest/seqkit_windows_amd64.exe.tar.gz.svg?maxAge=3600)](https://github.com/shenwei356/seqkit/releases/download/v0.16.0/seqkit_windows_amd64.exe.tar.gz)
 
 
 ## Installation
@@ -87,25 +101,41 @@ Run the following commands:
 
 ## Bash-completion
 
-Note: The current version supports Bash only.
-This should work for *nix systems with Bash installed.
+Supported shell: bash|zsh|fish|powershell
+
+Bash:
+
+    # generate completion shell
+    seqkit genautocomplete --shell bash
 
-Howto:
+    # configure if never did.
+    # install bash-completion if the "complete" command is not found.
+    echo "for bcfile in ~/.bash_completion.d/* ; do source \$bcfile; done" >> ~/.bash_completion
+    echo "source ~/.bash_completion" >> ~/.bashrc
 
-1. run: `seqkit genautocomplete`
+Zsh:
 
-2. create and edit `~/.bash_completion` file if you don't have it.
+    # generate completion shell
+    seqkit genautocomplete --shell zsh --file ~/.zfunc/_seqkit
 
-        nano ~/.bash_completion
+    # configure if never did
+    echo 'fpath=( ~/.zfunc "${fpath[@]}" )' >> ~/.zshrc
+    echo "autoload -U compinit; compinit" >> ~/.zshrc
 
-    add the following:
+fish:
 
-        for bcfile in ~/.bash_completion.d/* ; do
-          . $bcfile
-        done
+    seqkit genautocomplete --shell fish --file ~/.config/fish/completions/seqkit.fish
 
 ## Release History
 
+- [SeqKit v0.15.0](https://github.com/shenwei356/seqkit/releases/tag/v0.15.0)
+[![Github Releases (by Release)](https://img.shields.io/github/downloads/shenwei356/seqkit/v0.15.0/total.svg)](https://github.com/shenwei356/seqkit/releases/tag/v0.15.0)
+    - `seqkit grep/locate`: update help message.
+    - `seqkit grep`: **search on both strand when searching by sequence**.
+    - `seqkit split2`: fix redundant log when using `-s`.
+    - `seqkit bam`: new field `RightSoftClipSeq`. [#172](https://github.com/shenwei356/seqkit/pull/172)
+    - `seqkit sample -2`: remove extra `\n`. [#173](https://github.com/shenwei356/seqkit/issues/173)
+    - `seqkit split2 -l`: fix bug for splitting by accumulative length, this bug occurs when the first record is longer than `-l`, no sequences are lost.
 - [SeqKit v0.14.0](https://github.com/shenwei356/seqkit/releases/tag/v0.14.0)
 [![Github Releases (by Release)](https://img.shields.io/github/downloads/shenwei356/seqkit/v0.14.0/total.svg)](https://github.com/shenwei356/seqkit/releases/tag/v0.14.0)
     - new command `seqkit pair`: match up paired-end reads from two fastq files, faster than fastq-pair.


=====================================
doc/docs/usage.md
=====================================
@@ -39,6 +39,7 @@
 **Set operations**
 
 - [head](#head)
+- [head-genome](#head-genome)
 - [range](#range)
 - [sample](#sample)
 - [rmdup](#rmdup)
@@ -968,6 +969,7 @@ Flags:
   -a, --alphabet               print alphabet letters
   -q, --avg-qual               print average quality of a read
   -B, --base-content strings   print base content. (case ignored, multiple values supported) e.g. -B AT -B N
+  -C, --base-count strings     print base count. (case ignored, multiple values supported) e.g. -C AT -C N
   -I, --case-sensitive         calculate case sensitive base content
   -g, --gc                     print GC content
   -G, --gc-skew                print GC-Skew
@@ -1379,9 +1381,8 @@ Attentions:
      for partly matching.
   2. When searching by sequences, it's partly matching, and both positive
      and negative strands are searched.
-     Mismatch is allowed using flag "-m/--max-mismatch",
-     but it's not fast enough for large genome like human genome.
-     Though, it's fast enough for microbial genomes.
+     Mismatch is allowed using flag "-m/--max-mismatch", you can increase
+     the value of "-j/--threads" to accelerate processing.
   3. Degenerate bases/residues like "RYMM.." are also supported by flag -d.
      But do not use degenerate bases/residues in regular expression, you need
      convert them to regular expression, e.g., change "N" or "X"  to "."..
@@ -1424,6 +1425,7 @@ Flags:
       --delete-matched         delete a pattern right after being matched, this keeps the firstly matched data and speedups when using regular expressions
   -h, --help                   help for grep
   -i, --ignore-case            ignore case
+  -I, --immediate-output       print output immediately, do not use write buffer
   -v, --invert-match           invert the sense of matching, to select non-matching records
   -m, --max-mismatch int       max mismatch when matching by seq. For large genomes like human genome, using mapping/alignment tools would be faster
   -P, --only-positive-strand   only search on positive strand
@@ -1509,13 +1511,14 @@ Examples
         user    0m0.100s
         sys     0m0.017s
 
-        $ time cat hairpin.fa.gz | seqkit grep -s -i -p aggcg -m 1 | seqkit stats
+        $ time zcat hairpin.fa.gz | seqkit grep -s -i -p aggcg -m 1 | seqkit stats
         file  format  type  num_seqs    sum_len  min_len  avg_len  max_len
-        -     FASTA   RNA     17,168  1,881,005       39    109.6    2,354
+        -     FASTA   RNA     22,290  2,375,819       39    106.6    2,354
+
+        real    0m1.081s
+        user    0m1.305s
+        sys     0m0.158s
 
-        real    0m0.864s
-        user    0m0.941s
-        sys     0m0.014s
 
 1. Extract sequences starting with AGGCG
 
@@ -1553,8 +1556,7 @@ Attentions:
      parser accepts comma-separated-values (CSV) for multiple values (motifs).
      Patterns in file do not follow this rule.     
   4. Mismatch is allowed using flag "-m/--max-mismatch",
-     but it's not fast enough for large genome like human genome.
-     Though, it's fast enough for microbial genomes.
+     you can increase the value of "-j/--threads" to accelerate processing.
   5. When using flag --circular, end position of matched subsequence that 
      crossing genome sequence end would be greater than sequence length.
 
@@ -1569,6 +1571,7 @@ Flags:
   -h, --help                      help for locate
   -M, --hide-matched              do not show matched sequences
   -i, --ignore-case               ignore case
+  -I, --immediate-output          print output immediately, do not use write buffer
   -m, --max-mismatch int          max mismatch when matching by seq. For large genomes like human genome, using mapping/alignment tools would be faster
   -G, --non-greedy                non-greedy mode, faster but may miss motifs overlapping with others
   -P, --only-positive-strand      only search on positive strand
@@ -1777,6 +1780,7 @@ retrieve amplicon (or specific region around it) via primer(s).
 Attentions:
   1. Only one (the longest) matching location is returned for every primer pair.
   2. Mismatch is allowed, but the mismatch location (5' or 3') is not controled. 
+     You can increase the value of "-j/--threads" to accelerate processing.
   3. Degenerate bases/residues like "RYMM.." are also supported.
      But do not use degenerate bases/residues in regular expression, you need
      convert them to regular expression, e.g., change "N" or "X"  to "."..
@@ -1833,6 +1837,7 @@ Flags:
   -f, --flanking-region        region is flanking region
   -F, --forward string         forward primer (5'-primer-3'), degenerate bases allowed
   -h, --help                   help for amplicon
+  -I, --immediate-output       print output immediately, do not use write buffer
   -m, --max-mismatch int       max mismatch when matching primers, no degenerate bases allowed
   -P, --only-positive-strand   only search on positive strand
   -p, --primer-file string     3- or 2-column tabular primer file, with first column as primer name
@@ -2406,12 +2411,45 @@ Examples
         +
         HIHIIIIIHIIHGHHIHHIIIIIIIIIIIIIIIHHIIIIIHHIHIIIIIGIHIIIIHHHHHHGHIHIIIIIIIII
 
+## head-genome
+
+Usage
+
+```text
+
+print sequences of the first genome with common prefixes in name
+
+For a FASTA file containing multiple contigs of strains (see example below),
+these's no list of IDs available for retrieving sequences of a certain strain,
+while descriptions of each strain share the same prefix.
+
+This command is used to restrieve sequences of the first strain,
+i.e., "Vibrio cholerae strain M29".
+
+>NZ_JFGR01000001.1 Vibrio cholerae strain M29 Contig_1, whole genome shotgun sequence
+>NZ_JFGR01000002.1 Vibrio cholerae strain M29 Contig_2, whole genome shotgun sequence
+>NZ_JFGR01000003.1 Vibrio cholerae strain M29 Contig_3, whole genome shotgun sequence
+>NZ_JSTP01000001.1 Vibrio cholerae strain 2012HC-12 NODE_79, whole genome shotgun sequence
+>NZ_JSTP01000002.1 Vibrio cholerae strain 2012HC-12 NODE_78, whole genome shotgun sequence
+
+Attention:
+
+  1. Sequences in file should be well organized.
+
+Usage:
+  seqkit head-genome [flags]
+
+Flags:
+  -h, --help                    help for head-genome
+  -m, --mini-common-words int   minimal shared prefix words (default 4)
 
+```
+        
 ## range
 
 Usage
 
-``` text
+```text
 print FASTA/Q records in a range (start:end)
 
 Usage:
@@ -3338,22 +3376,30 @@ Usage
 ``` text
 generate shell autocompletion script
 
-Note: The current version supports Bash only.
-This should work for *nix systems with Bash installed.
+Supported shell: bash|zsh|fish|powershell
+
+Bash:
+
+    # generate completion shell
+    seqkit genautocomplete --shell bash
 
-Howto:
+    # configure if never did.
+    # install bash-completion if the "complete" command is not found.
+    echo "for bcfile in ~/.bash_completion.d/* ; do source \$bcfile; done" >> ~/.bash_completion
+    echo "source ~/.bash_completion" >> ~/.bashrc
 
-1. run: seqkit genautocomplete
+Zsh:
 
-2. create and edit ~/.bash_completion file if you don't have it.
+    # generate completion shell
+    seqkit genautocomplete --shell zsh --file ~/.zfunc/_seqkit
 
-        nano ~/.bash_completion
+    # configure if never did
+    echo 'fpath=( ~/.zfunc "${fpath[@]}" )' >> ~/.zshrc
+    echo "autoload -U compinit; compinit" >> ~/.zshrc
 
-   add the following:
+fish:
 
-        for bcfile in ~/.bash_completion.d/* ; do
-          . $bcfile
-        done
+    seqkit genautocomplete --shell fish --file ~/.config/fish/completions/seqkit.fish
 
 Usage:
   seqkit genautocomplete [flags]


=====================================
doc/mkdocs.yml
=====================================
@@ -1,5 +1,5 @@
 site_name: SeqKit - Ultrafast FASTA/Q kit
-pages:
+nav:
 - Home: index.md
 - Download: download.md
 - Documents:
@@ -12,17 +12,17 @@ pages:
 # theme: cinder
 
 theme:
-    name: 'material'
+    name: material
     palette:
-        primary: 'teal'
-        accent: 'blue grey'
-    logo:
-        icon: 'school'
-    favicon: 'files/favicon.ico'
+        primary: teal
+        accent: blue grey
+    icon:
+        logo: material/school
+    favicon: files/favicon.ico
     feature:
-        tabs: false
+        navigation.tabs: false
 extra:
-  manifest: 'manifest.webmanifest'
+  manifest: manifest.webmanifest
 
 repo_url: https://github.com/shenwei356/seqkit
 site_description: SeqKit - a cross-platform and ultrafast toolkit for FASTA/Q file manipulation


=====================================
go.mod
=====================================
@@ -7,30 +7,25 @@ require (
 	github.com/biogo/hts v1.2.2
 	github.com/bsipos/thist v1.0.0
 	github.com/cespare/xxhash v1.1.0
-	github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
 	github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8
 	github.com/dustin/go-humanize v1.0.0
-	github.com/edsrzf/mmap-go v1.0.0 // indirect
 	github.com/fsnotify/fsnotify v1.4.9
 	github.com/iafan/cwalk v0.0.0-20191125092548-dd7f505d2f66
-	github.com/klauspost/compress v1.11.4 // indirect
-	github.com/klauspost/pgzip v1.2.5 // indirect
 	github.com/logrusorgru/aurora v2.0.3+incompatible
 	github.com/mattn/go-colorable v0.1.8
 	github.com/mattn/go-isatty v0.0.12
 	github.com/mattn/go-runewidth v0.0.9 // indirect
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/pkg/errors v0.9.1
-	github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
-	github.com/shenwei356/bio v0.0.0-20201213090627-18e3e643a476
-	github.com/shenwei356/bpool v0.0.0-20160710042833-f9e0ee4d0403 // indirect
-	github.com/shenwei356/breader v0.0.0-20170924140440-21f0a70fe179
-	github.com/shenwei356/bwt v0.0.0-20200418151221-ae79c9858c90
+	github.com/shenwei356/bio v0.1.0
+	github.com/shenwei356/breader v0.1.0
+	github.com/shenwei356/bwt v0.5.1
 	github.com/shenwei356/go-logging v0.0.0-20171012171522-c6b9702d88ba
 	github.com/shenwei356/natsort v0.0.0-20190418160752-600d539c017d // indirect
-	github.com/shenwei356/util v0.0.0-20201214054755-2942125340cd
+	github.com/shenwei356/util v0.3.0
 	github.com/shenwei356/xopen v0.0.0-20181203091311-f4f16ddd3992
 	github.com/smallfish/simpleyaml v0.0.0-20170911015856-a32031077861
-	github.com/spf13/cobra v1.1.1
+	github.com/spf13/cobra v1.1.3
 	github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939
+	github.com/twotwotwo/sorts v0.0.0-20160814051341-bf5c1f2b8553
 )


=====================================
go.sum
=====================================
@@ -139,8 +139,9 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
 github.com/kortschak/utter v0.0.0-20190412033250-50fe362e6560/go.mod h1:oDr41C7kH9wvAikWyFhr6UFr8R7nelpmCF5XR5rL7I8=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -194,20 +195,20 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/shenwei356/bio v0.0.0-20201213090627-18e3e643a476 h1:PE3sUBFeHWFNLsrQ7Blyk4duI+emVGKQ1+iYo4ZalKk=
-github.com/shenwei356/bio v0.0.0-20201213090627-18e3e643a476/go.mod h1:tKIebGVfqYwgLIoXohkLiQO685B2d3l9vTf1cjRZaVk=
+github.com/shenwei356/bio v0.1.0 h1:VDnI28zcdybywdn6/tcZvplAJ1IxOAAYrTJhhTB1SLQ=
+github.com/shenwei356/bio v0.1.0/go.mod h1:NgFauYHlpmjCYEf2XP8foITht6ej6poggQkILpjraN4=
 github.com/shenwei356/bpool v0.0.0-20160710042833-f9e0ee4d0403 h1:/3JklLnHXiWUBxWc3joQYavDQJpncRhRA909cUb7eOw=
 github.com/shenwei356/bpool v0.0.0-20160710042833-f9e0ee4d0403/go.mod h1:YkgdTWfNnJgv5HVJbVSDmxQtkK3/jZWDoqcG26BVU8k=
-github.com/shenwei356/breader v0.0.0-20170924140440-21f0a70fe179 h1:MZiEG6aN8j1A0jXzCzyZM2BM6ZHbePYiwR4FXgUTCCk=
-github.com/shenwei356/breader v0.0.0-20170924140440-21f0a70fe179/go.mod h1:Yb9qYJ2qJolWeqIVS1LG5ylHTLI5ZfbVFEJJouF2DsY=
-github.com/shenwei356/bwt v0.0.0-20200418151221-ae79c9858c90 h1:c8Er9GmVclwt002/ZxVx7WOThpmPKNR4rehAXxICTII=
-github.com/shenwei356/bwt v0.0.0-20200418151221-ae79c9858c90/go.mod h1:q4esDgocpgU8CcnXuPNun+hGv+VDXGX+NnXHQaHGRCU=
+github.com/shenwei356/breader v0.1.0 h1:g7q33VI15Mroj2TTab06CF6prbQAez+6p8fAoK4qkjo=
+github.com/shenwei356/breader v0.1.0/go.mod h1:YXIrHIPtbJCP6Kv27qGp+cXQl7hyzD0iQrEVYCy/gqw=
+github.com/shenwei356/bwt v0.5.1 h1:pq9KxkJPwzUwPjwWqfTOkUmzwHm8JtMkZxBkqkzuIVo=
+github.com/shenwei356/bwt v0.5.1/go.mod h1:V2hX4adhr4WfFpy2ogKiV8A2WO9FeK0wkq1o3/R91mE=
 github.com/shenwei356/go-logging v0.0.0-20171012171522-c6b9702d88ba h1:UvnrxFDPmz7agYX0eQ2JEorTKn1ORnZ9dT5OzbjPvK8=
 github.com/shenwei356/go-logging v0.0.0-20171012171522-c6b9702d88ba/go.mod h1:LiqYp/K5yCEWOi7Ux/iOF/kjDxtsdYjOGcKHDbEOXFU=
 github.com/shenwei356/natsort v0.0.0-20190418160752-600d539c017d h1:eeXLHcXyGEr72V1SOSEI7vSzUOTJvHutwF7Ykm+hscQ=
 github.com/shenwei356/natsort v0.0.0-20190418160752-600d539c017d/go.mod h1:SiiGiRFyRtV7S9RamOrmQR5gpGIRhWJM1w0EtmuQ1io=
-github.com/shenwei356/util v0.0.0-20201214054755-2942125340cd h1:11sa/TyR/91v63OA9VnyjRontqtsDFZN1wCLNaaAa38=
-github.com/shenwei356/util v0.0.0-20201214054755-2942125340cd/go.mod h1:n3qhc3bQzlqJ2/5v79hgl0Gd3WzJOkI8XcUix25Brdg=
+github.com/shenwei356/util v0.3.0 h1:gTVa3sGwcyGEHgNpXTzdL3MaaJN/bGAypVKSCnT4QfU=
+github.com/shenwei356/util v0.3.0/go.mod h1:n3qhc3bQzlqJ2/5v79hgl0Gd3WzJOkI8XcUix25Brdg=
 github.com/shenwei356/xopen v0.0.0-20181203091311-f4f16ddd3992 h1:RXEEyKj0JL3SrRIYsWIEyy4AwjHbI3I8aDGK6CA4+YI=
 github.com/shenwei356/xopen v0.0.0-20181203091311-f4f16ddd3992/go.mod h1:6EQUa6I7Zsl2GQKqcL9qGLrTzVE+oZyly+uhzovQYSk=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -221,8 +222,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
+github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
+github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -236,6 +237,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
 github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939 h1:BhIUXV2ySTLrKgh/Hnts+QTQlIbWtomXt3LMdzME0A0=
 github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939/go.mod h1:omGxs4/6hNjxPKUTjmaNkPzehSnNJOJN6pMEbrlYIT4=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/twotwotwo/sorts v0.0.0-20160814051341-bf5c1f2b8553 h1:DRC1ubdb3ZmyyIeCSTxjZIQAnpLPfKVgYrLETQuOPjo=
+github.com/twotwotwo/sorts v0.0.0-20160814051341-bf5c1f2b8553/go.mod h1:Rj7Csq/tZ/egz+Ltc2IVpsA5309AmSMEswjkTZmq2Xc=
 github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -312,8 +315,9 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -367,16 +371,17 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=


=====================================
seqkit/cmd/amplicon.go
=====================================
@@ -31,6 +31,7 @@ import (
 	"sort"
 	"strconv"
 	"strings"
+	"sync"
 
 	"github.com/shenwei356/bio/featio/gtf"
 	"github.com/shenwei356/bio/seq"
@@ -40,6 +41,7 @@ import (
 	"github.com/shenwei356/bwt/fmi"
 	"github.com/shenwei356/xopen"
 	"github.com/spf13/cobra"
+	"github.com/twotwotwo/sorts/sortutil"
 )
 
 // ampliconCmd represents the amplicon command
@@ -51,6 +53,7 @@ var ampliconCmd = &cobra.Command{
 Attentions:
   1. Only one (the longest) matching location is returned for every primer pair.
   2. Mismatch is allowed, but the mismatch location (5' or 3') is not controled. 
+     You can increase the value of "-j/--threads" to accelerate processing.
   3. Degenerate bases/residues like "RYMM.." are also supported.
      But do not use degenerate bases/residues in regular expression, you need
      convert them to regular expression, e.g., change "N" or "X"  to "."..
@@ -131,6 +134,8 @@ Examples:
 		onlyPositiveStrand := getFlagBool(cmd, "only-positive-strand")
 		outFmtBED := getFlagBool(cmd, "bed")
 
+		immediateOutput := getFlagBool(cmd, "immediate-output")
+
 		var list [][3]string
 		var primers [][3][]byte
 
@@ -180,13 +185,194 @@ Examples:
 			usingRegion = true
 		}
 
+		strands := []string{"+", "-"}
+
+		// -------------------------------------------------------------------
+		// only for m > 0, where FMI is slow
+
+		if maxMismatch > 0 {
+			type Arecord struct {
+				id     uint64
+				ok     bool
+				record []string
+			}
+
+			var wg sync.WaitGroup
+			ch := make(chan *Arecord, config.Threads)
+			tokens := make(chan int, config.Threads)
+
+			done := make(chan int)
+			go func() {
+				m := make(map[uint64]*Arecord, config.Threads)
+				var id, _id uint64
+				var ok bool
+				var _r *Arecord
+				var row string
+
+				id = 1
+				for r := range ch {
+					_id = r.id
+
+					if _id == id { // right there
+						if r.ok {
+							for _, row = range r.record {
+								outfh.WriteString(row)
+							}
+
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+						id++
+						continue
+					}
+
+					m[_id] = r // save for later check
+
+					if _r, ok = m[id]; ok { // check buffered
+						if _r.ok {
+							for _, row = range _r.record {
+								outfh.WriteString(row)
+							}
+
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+						delete(m, id)
+						id++
+					}
+				}
+
+				if len(m) > 0 {
+					ids := make([]uint64, len(m))
+					i := 0
+					for _id = range m {
+						ids[i] = _id
+						i++
+					}
+					sortutil.Uint64s(ids)
+					for _, _id = range ids {
+						_r = m[_id]
+
+						if _r.ok {
+							for _, row = range _r.record {
+								outfh.WriteString(row)
+							}
+
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+					}
+				}
+				done <- 1
+			}()
+
+			var id uint64
+			for _, file := range files {
+				var record *fastx.Record
+				var fastxReader *fastx.Reader
+
+				fastxReader, err = fastx.NewReader(alphabet, file, idRegexp)
+				checkError(err)
+
+				for {
+					record, err = fastxReader.Read()
+					if err != nil {
+						if err == io.EOF {
+							break
+						}
+						checkError(err)
+						break
+					}
+					if fastxReader.IsFastq {
+						config.LineWidth = 0
+						fastx.ForcelyOutputFastq = true
+					}
+
+					tokens <- 1
+					wg.Add(1)
+					id++
+					go func(record *fastx.Record, id uint64) {
+						defer func() {
+							wg.Done()
+							<-tokens
+						}()
+
+						var finder *AmpliconFinder
+						var loc []int
+						var strand string
+						var tmpSeq *seq.Seq
+						var primer [3][]byte
+
+						results := make([]string, 0, 2)
+
+						for _, strand = range strands {
+							if strand == "-" {
+								if onlyPositiveStrand {
+									continue
+								}
+								record.Seq.RevComInplace()
+							}
+
+							for _, primer = range primers {
+								finder, err = NewAmpliconFinder(record.Seq.Seq, primer[1], primer[2], maxMismatch)
+								checkError(err)
+
+								if usingRegion {
+									loc, err = finder.LocateRange(begin, end, fregion, strict)
+								} else {
+									loc, err = finder.Locate()
+								}
+								checkError(err)
+
+								if loc == nil {
+									continue
+								}
+
+								if outFmtBED {
+									results = append(results, fmt.Sprintf("%s\t%d\t%d\t%s\t%d\t%s\t%s\n",
+										record.ID,
+										loc[0]-1,
+										loc[1],
+										primer[0],
+										0,
+										strand,
+										record.Seq.SubSeq(loc[0], loc[1]).Seq))
+
+									continue
+								}
+								tmpSeq = record.Seq
+
+								record.Seq = record.Seq.SubSeq(loc[0], loc[1])
+
+								results = append(results, string(record.Format(config.LineWidth)))
+
+								record.Seq = tmpSeq
+							}
+						}
+
+						ch <- &Arecord{record: results, id: id, ok: len(results) > 0}
+					}(record.Clone(), id)
+				}
+
+				config.LineWidth = lineWidth
+			}
+
+			wg.Wait()
+			close(ch)
+			<-done
+
+			return
+		}
+
 		var record *fastx.Record
 		var fastxReader *fastx.Reader
 
 		var finder *AmpliconFinder
 		var loc []int
 
-		strands := []string{"+", "-"}
 		var strand string
 		var tmpSeq *seq.Seq
 		var primer [3][]byte
@@ -271,7 +457,8 @@ func init() {
 	ampliconCmd.Flags().BoolP("flanking-region", "f", false, "region is flanking region")
 	ampliconCmd.Flags().BoolP("strict-mode", "s", false, "strict mode, i.e., discarding seqs not fully matching (shorter) given region range")
 	ampliconCmd.Flags().BoolP("only-positive-strand", "P", false, "only search on positive strand")
-	ampliconCmd.Flags().BoolP("bed", "", false, "output in BED6+1 format with amplicon as 7th columns")
+	ampliconCmd.Flags().BoolP("bed", "", false, "output in BED6+1 format with amplicon as the 7th column")
+	ampliconCmd.Flags().BoolP("immediate-output", "I", false, "print output immediately, do not use write buffer")
 }
 
 func loadPrimers(file string) ([][3]string, error) {
@@ -691,7 +878,7 @@ func (finder *AmpliconFinder) Locate() ([]int, error) {
 	sort.Ints(locsI) // to remain the FIRST location
 	sort.Ints(locsJ) // to remain the LAST location
 	finder.searched, finder.found = true, true
-	finder.iBegin, finder.iEnd = locsI[0], locsI[0]+len(finder.R)-1
+	finder.iBegin, finder.iEnd = locsI[0], locsJ[len(locsJ)-1]+len(finder.R)-1
 	return []int{locsI[0] + 1, locsJ[len(locsJ)-1] + len(finder.R)}, nil
 }
 


=====================================
seqkit/cmd/bam_toolbox.go
=====================================
@@ -58,7 +58,7 @@ type Toolshed map[string]BamTool
 
 func (s Toolshed) String() string {
 	tools := make([]string, 0, len(s))
-	for t, _ := range s {
+	for t := range s {
 		tools = append(tools, t)
 	}
 	sort.Strings(tools)


=====================================
seqkit/cmd/bed.go
=====================================
@@ -28,7 +28,6 @@ import (
 	"strings"
 
 	"github.com/shenwei356/breader"
-	"github.com/shenwei356/util/stringutil"
 )
 
 // BedFeature is the gff BedFeature struct
@@ -64,9 +63,8 @@ func ReadBedFilteredFeatures(file string, chrs []string) ([]BedFeature, error) {
 		if line == "" || line[0] == '#' || (len(line) > 7 && string(line[0:7]) == "browser") || (len(line) > 5 && string(line[0:5]) == "track") {
 			return nil, false, nil
 		}
-		//
-		// do not use regexp.Split(), it's very slow
-		items := stringutil.Split(line, "\t")
+
+		items := strings.Split(line, "\t")
 		n := len(items)
 		if n < 3 {
 			return nil, false, nil


=====================================
seqkit/cmd/fx2tab.go
=====================================
@@ -63,6 +63,7 @@ Attention:
 		printGC := getFlagBool(cmd, "gc")
 		printGCSkew := getFlagBool(cmd, "gc-skew")
 		baseContents := getFlagStringSlice(cmd, "base-content")
+		baseCounts := getFlagStringSlice(cmd, "base-count")
 		caseSensitive := getFlagBool(cmd, "case-sensitive")
 		onlyName := getFlagBool(cmd, "name")
 		printTitle := getFlagBool(cmd, "header-line")
@@ -98,6 +99,11 @@ Attention:
 			if printGCSkew {
 				outfh.WriteString("\tGC-Skew")
 			}
+			if len(baseCounts) > 0 {
+				for _, bc := range baseCounts {
+					outfh.WriteString(fmt.Sprintf("\t%s", bc))
+				}
+			}
 			if len(baseContents) > 0 {
 				for _, bc := range baseContents {
 					outfh.WriteString(fmt.Sprintf("\t%s", bc))
@@ -164,6 +170,16 @@ Attention:
 					outfh.WriteString(fmt.Sprintf("\t%.2f", (g-c)/(g+c)*100))
 				}
 
+				if len(baseCounts) > 0 {
+					for _, bc := range baseCounts {
+						if caseSensitive {
+							outfh.WriteString(fmt.Sprintf("\t%d", record.Seq.BaseCountCaseSensitive(bc)))
+						} else {
+							outfh.WriteString(fmt.Sprintf("\t%d", record.Seq.BaseCount(bc)))
+						}
+					}
+				}
+
 				if len(baseContents) > 0 {
 					for _, bc := range baseContents {
 						if caseSensitive {
@@ -199,6 +215,7 @@ func init() {
 	fx2tabCmd.Flags().BoolP("gc", "g", false, "print GC content")
 	fx2tabCmd.Flags().BoolP("gc-skew", "G", false, "print GC-Skew")
 	fx2tabCmd.Flags().StringSliceP("base-content", "B", []string{}, "print base content. (case ignored, multiple values supported) e.g. -B AT -B N")
+	fx2tabCmd.Flags().StringSliceP("base-count", "C", []string{}, "print base count. (case ignored, multiple values supported) e.g. -C AT -C N")
 	fx2tabCmd.Flags().BoolP("case-sensitive", "I", false, "calculate case sensitive base content")
 	fx2tabCmd.Flags().BoolP("only-id", "i", false, "print ID instead of full head")
 	fx2tabCmd.Flags().BoolP("name", "n", false, "only print names (no sequences and qualities)")


=====================================
seqkit/cmd/genautocomplete.go
=====================================
@@ -1,4 +1,4 @@
-// Copyright © 2016-2019 Wei Shen <shenwei356 at gmail.com>
+// Copyright © 2016-2021 Wei Shen <shenwei356 at gmail.com>
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -33,44 +33,60 @@ import (
 // genautocompleteCmd represents the fq2fa command
 var genautocompleteCmd = &cobra.Command{
 	Use:   "genautocomplete",
-	Short: "generate shell autocompletion script",
+	Short: "generate shell autocompletion script (bash|zsh|fish|powershell)",
 	Long: `generate shell autocompletion script
 
-Note: The current version supports Bash only.
-This should work for *nix systems with Bash installed.
+Supported shell: bash|zsh|fish|powershell
 
-Howto:
+Bash:
 
-1. run: seqkit genautocomplete
+    # generate completion shell
+    seqkit genautocomplete --shell bash
 
-2. create and edit ~/.bash_completion file if you don't have it.
+    # configure if never did.
+    # install bash-completion if the "complete" command is not found.
+    echo "for bcfile in ~/.bash_completion.d/* ; do source \$bcfile; done" >> ~/.bash_completion
+    echo "source ~/.bash_completion" >> ~/.bashrc
 
-        nano ~/.bash_completion
+Zsh:
 
-    add the following:
+    # generate completion shell
+    seqkit genautocomplete --shell zsh --file ~/.zfunc/_seqkit
 
-        for bcfile in ~/.bash_completion.d/* ; do
-          . $bcfile
-        done
+    # configure if never did
+    echo 'fpath=( ~/.zfunc "${fpath[@]}" )' >> ~/.zshrc
+    echo "autoload -U compinit; compinit" >> ~/.zshrc
+
+fish:
+
+    seqkit genautocomplete --shell fish --file ~/.config/fish/completions/seqkit.fish
 
 `,
 	Run: func(cmd *cobra.Command, args []string) {
-		autocompleteTarget := getFlagString(cmd, "file")
-		autocompleteType := getFlagString(cmd, "type")
-
-		if autocompleteType != "bash" {
-			checkError(fmt.Errorf("only Bash is supported for now"))
-		}
+		outfile := getFlagString(cmd, "file")
+		shell := getFlagString(cmd, "shell")
 
-		dir := filepath.Dir(autocompleteTarget)
+		dir := filepath.Dir(outfile)
 		ok, err := pathutil.DirExists(dir)
 		checkError(err)
 		if !ok {
 			os.MkdirAll(dir, 0744)
 		}
-		checkError(cmd.Root().GenBashCompletionFile(autocompleteTarget))
 
-		log.Infof("bash completion file for SeqKit saved to %s", autocompleteTarget)
+		switch shell {
+		case "bash":
+			checkError(cmd.Root().GenBashCompletionFile(outfile))
+		case "zsh":
+			checkError(cmd.Root().GenZshCompletionFile(outfile))
+		case "fish":
+			checkError(cmd.Root().GenFishCompletionFile(outfile, true))
+		case "powershell":
+			checkError(cmd.Root().GenPowerShellCompletionFile(outfile))
+		default:
+			checkError(fmt.Errorf("unsupported shell: %s", shell))
+		}
+
+		log.Infof("%s completion file for seqkit saved to %s", shell, outfile)
 	},
 }
 
@@ -79,5 +95,5 @@ func init() {
 	defaultCompletionFile, err := homedir.Expand("~/.bash_completion.d/seqkit.sh")
 	checkError(err)
 	genautocompleteCmd.Flags().StringP("file", "", defaultCompletionFile, "autocompletion file")
-	genautocompleteCmd.Flags().StringP("type", "", "bash", "autocompletion type (currently only bash supported)")
+	genautocompleteCmd.Flags().StringP("shell", "", "bash", "autocompletion type (bash|zsh|fish|powershell)")
 }


=====================================
seqkit/cmd/grep.go
=====================================
@@ -28,8 +28,10 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"sync"
 
 	"github.com/shenwei356/bwt"
+	"github.com/twotwotwo/sorts/sortutil"
 
 	"github.com/shenwei356/bio/seq"
 	"github.com/shenwei356/bio/seqio/fastx"
@@ -54,9 +56,8 @@ Attentions:
      for partly matching.
   2. When searching by sequences, it's partly matching, and both positive
      and negative strands are searched.
-     Mismatch is allowed using flag "-m/--max-mismatch",
-     but it's not fast enough for large genome like human genome.
-     Though, it's fast enough for microbial genomes.
+     Mismatch is allowed using flag "-m/--max-mismatch", you can increase
+     the value of "-j/--threads" to accelerate processing.
   3. Degenerate bases/residues like "RYMM.." are also supported by flag -d.
      But do not use degenerate bases/residues in regular expression, you need
      convert them to regular expression, e.g., change "N" or "X"  to "."..
@@ -105,6 +106,8 @@ Examples:
 		region := getFlagString(cmd, "region")
 		circular := getFlagBool(cmd, "circular")
 
+		immediateOutput := getFlagBool(cmd, "immediate-output")
+
 		if len(pattern) == 0 && patternFile == "" {
 			checkError(fmt.Errorf("one of flags -p (--pattern) and -f (--pattern-file) needed"))
 		}
@@ -266,21 +269,209 @@ Examples:
 		checkError(err)
 		defer outfh.Close()
 
+		var fastxReader *fastx.Reader
+		var record *fastx.Record
+		strands := []byte{'+', '-'}
+
+		// -------------------------------------------------------------------
+		// only for searching with sequences and mismatch > 0, were FMI is very slow
+
+		if bySeq && mismatches > 1 {
+			type Arecord struct {
+				id     uint64
+				ok     bool
+				record *fastx.Record
+			}
+
+			var wg sync.WaitGroup
+			ch := make(chan *Arecord, config.Threads)
+			tokens := make(chan int, config.Threads)
+
+			done := make(chan int)
+			go func() {
+				m := make(map[uint64]*Arecord, config.Threads)
+				var id, _id uint64
+				var ok bool
+				var _r *Arecord
+
+				id = 1
+				for r := range ch {
+					_id = r.id
+
+					if _id == id { // right there
+						if r.ok {
+							r.record.FormatToWriter(outfh, config.LineWidth)
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+						id++
+						continue
+					}
+
+					m[_id] = r // save for later check
+
+					if _r, ok = m[id]; ok { // check buffered
+						if _r.ok {
+							_r.record.FormatToWriter(outfh, config.LineWidth)
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+						delete(m, id)
+						id++
+					}
+				}
+
+				if len(m) > 0 {
+					ids := make([]uint64, len(m))
+					i := 0
+					for _id = range m {
+						ids[i] = _id
+						i++
+					}
+					sortutil.Uint64s(ids)
+					for _, _id = range ids {
+						_r = m[_id]
+
+						if _r.ok {
+							_r.record.FormatToWriter(outfh, config.LineWidth)
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+					}
+				}
+				done <- 1
+			}()
+
+			var id uint64
+			for _, file := range files {
+				fastxReader, err = fastx.NewReader(alphabet, file, idRegexp)
+				checkError(err)
+
+				checkAlphabet := true
+				for {
+					record, err = fastxReader.Read()
+					if err != nil {
+						if err == io.EOF {
+							break
+						}
+						checkError(err)
+						break
+					}
+
+					if checkAlphabet {
+						if fastxReader.Alphabet() == seq.Unlimit || fastxReader.Alphabet() == seq.Protein {
+							onlyPositiveStrand = true
+						}
+						checkAlphabet = false
+					}
+
+					if fastxReader.IsFastq {
+						config.LineWidth = 0
+						fastx.ForcelyOutputFastq = true
+					}
+
+					tokens <- 1
+					wg.Add(1)
+					id++
+					go func(record *fastx.Record, id uint64) {
+						defer func() {
+							wg.Done()
+							<-tokens
+						}()
+
+						var sequence *seq.Seq
+						var target []byte
+						var hit bool
+						var k string
+
+						sfmi := fmi.NewFMIndex()
+
+						for _, strand := range strands {
+							if hit {
+								break
+							}
+
+							if strand == '-' && onlyPositiveStrand {
+								break
+							}
+
+							sequence = record.Seq
+							if strand == '-' {
+								sequence = record.Seq.RevCom()
+							}
+							if limitRegion {
+								target = sequence.SubSeq(start, end).Seq
+							} else if circular {
+								// concat two copies of sequence, and do not change orginal sequence
+								target = make([]byte, len(sequence.Seq)*2)
+								copy(target[0:len(sequence.Seq)], sequence.Seq)
+								copy(target[len(sequence.Seq):], sequence.Seq)
+							} else {
+								target = sequence.Seq
+							}
+
+							if ignoreCase {
+								target = bytes.ToLower(target)
+							}
+
+							_, err = sfmi.Transform(target)
+							if err != nil {
+								checkError(fmt.Errorf("fail to build FMIndex for sequence: %s", record.Name))
+							}
+							for k = range patterns {
+								hit, err = sfmi.Match([]byte(k), mismatches)
+								if err != nil {
+									checkError(fmt.Errorf("fail to search pattern '%s' on seq '%s': %s", k, record.Name, err))
+								}
+								if hit {
+									break
+								}
+							}
+
+						}
+
+						if invertMatch {
+							if hit {
+								ch <- &Arecord{record: nil, ok: false, id: id}
+								return
+							}
+						} else {
+							if !hit {
+								ch <- &Arecord{record: nil, ok: false, id: id}
+								return
+							}
+						}
+
+						ch <- &Arecord{record: record, ok: true, id: id}
+
+					}(record.Clone(), id)
+				}
+			}
+
+			wg.Wait()
+			close(ch)
+			<-done
+
+			return
+		}
+
+		// -------------------------------------------------------------------
+
 		var sequence *seq.Seq
 		var target []byte
 		var ok, hit bool
-		var record *fastx.Record
-		var fastxReader *fastx.Reader
 		var k string
-		var locs []int
 		var re *regexp.Regexp
 		var p string
-		strands := []byte{'+', '-'}
 		var strand byte
 		for _, file := range files {
 			fastxReader, err = fastx.NewReader(alphabet, file, idRegexp)
 			checkError(err)
 
+			checkAlphabet := true
 			for {
 				record, err = fastxReader.Read()
 				if err != nil {
@@ -290,6 +481,14 @@ Examples:
 					checkError(err)
 					break
 				}
+
+				if checkAlphabet {
+					if fastxReader.Alphabet() == seq.Unlimit || fastxReader.Alphabet() == seq.Protein {
+						onlyPositiveStrand = true
+					}
+					checkAlphabet = false
+				}
+
 				if fastxReader.IsFastq {
 					config.LineWidth = 0
 					fastx.ForcelyOutputFastq = true
@@ -367,12 +566,11 @@ Examples:
 								checkError(fmt.Errorf("fail to build FMIndex for sequence: %s", record.Name))
 							}
 							for k = range patterns {
-								locs, err = sfmi.Locate([]byte(k), mismatches)
+								hit, err = sfmi.Match([]byte(k), mismatches)
 								if err != nil {
 									checkError(fmt.Errorf("fail to search pattern '%s' on seq '%s': %s", k, record.Name, err))
 								}
-								if len(locs) > 0 {
-									hit = true
+								if hit {
 									if deleteMatched && !invertMatch {
 										delete(patterns, k)
 									}
@@ -406,6 +604,10 @@ Examples:
 				}
 
 				record.FormatToWriter(outfh, config.LineWidth)
+
+				if immediateOutput {
+					outfh.Flush()
+				}
 			}
 
 			config.LineWidth = lineWidth
@@ -430,4 +632,5 @@ func init() {
 	grepCmd.Flags().StringP("region", "R", "", "specify sequence region for searching. "+
 		"e.g 1:12 for first 12 bases, -12:-1 for last 12 bases")
 	grepCmd.Flags().BoolP("circular", "c", false, "circular genome")
+	grepCmd.Flags().BoolP("immediate-output", "I", false, "print output immediately, do not use write buffer")
 }


=====================================
seqkit/cmd/head-genome.go
=====================================
@@ -0,0 +1,151 @@
+// Copyright © 2016-2019 Wei Shen <shenwei356 at gmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package cmd
+
+import (
+	"fmt"
+	"io"
+	"runtime"
+
+	"github.com/shenwei356/bio/seq"
+	"github.com/shenwei356/bio/seqio/fastx"
+	"github.com/shenwei356/util/stringutil"
+	"github.com/shenwei356/xopen"
+	"github.com/spf13/cobra"
+)
+
+// headGenomeCmd represents the head command
+var headGenomeCmd = &cobra.Command{
+	Use:   "head-genome",
+	Short: "print sequences of the first genome with common prefixes in name",
+	Long: `print sequences of the first genome with common prefixes in name
+
+For a FASTA file containing multiple contigs of strains (see example below),
+these's no list of IDs available for retrieving sequences of a certain strain,
+while descriptions of each strain share the same prefix.
+
+This command is used to restrieve sequences of the first strain,
+i.e., "Vibrio cholerae strain M29".
+
+>NZ_JFGR01000001.1 Vibrio cholerae strain M29 Contig_1, whole genome shotgun sequence
+>NZ_JFGR01000002.1 Vibrio cholerae strain M29 Contig_2, whole genome shotgun sequence
+>NZ_JFGR01000003.1 Vibrio cholerae strain M29 Contig_3, whole genome shotgun sequence
+>NZ_JSTP01000001.1 Vibrio cholerae strain 2012HC-12 NODE_79, whole genome shotgun sequence
+>NZ_JSTP01000002.1 Vibrio cholerae strain 2012HC-12 NODE_78, whole genome shotgun sequence
+
+Attention:
+
+  1. Sequences in file should be well organized.
+
+`,
+	Run: func(cmd *cobra.Command, args []string) {
+		config := getConfigs(cmd)
+		alphabet := config.Alphabet
+		idRegexp := config.IDRegexp
+		outFile := config.OutFile
+		lineWidth := config.LineWidth
+		seq.AlphabetGuessSeqLengthThreshold = config.AlphabetGuessSeqLength
+		seq.ValidateSeq = false
+		runtime.GOMAXPROCS(config.Threads)
+
+		minWords := getFlagPositiveInt(cmd, "mini-common-words")
+
+		files := getFileListFromArgsAndFile(cmd, args, true, "infile-list", true)
+
+		outfh, err := xopen.Wopen(outFile)
+		checkError(err)
+		defer outfh.Close()
+
+		var record *fastx.Record
+		var fastxReader *fastx.Reader
+
+		var prefixes, words []string
+		var i, N int
+		var nSharedWords, pNSharedWords int
+
+		for _, file := range files {
+			fastxReader, err = fastx.NewReader(alphabet, file, idRegexp)
+			checkError(err)
+
+			for {
+				record, err = fastxReader.Read()
+				if err != nil {
+					if err == io.EOF {
+						break
+					}
+					checkError(err)
+					break
+				}
+
+				if fastxReader.IsFastq {
+					config.LineWidth = 0
+					fastx.ForcelyOutputFastq = true
+				}
+
+				if len(record.Desc) == 0 {
+					checkError(fmt.Errorf("no description: %s", record.ID))
+				}
+
+				if prefixes == nil { // first record
+					record.FormatToWriter(outfh, config.LineWidth)
+
+					prefixes = stringutil.Split(string(record.Desc), "\t ")
+					continue
+				}
+
+				words = stringutil.Split(string(record.Desc), "\t ")
+				if len(words) < len(prefixes) {
+					N = len(words)
+				} else {
+					N = len(prefixes)
+				}
+
+				nSharedWords = 0
+				for i = 0; i < N; i++ {
+					if words[i] != prefixes[i] {
+						break
+					}
+					nSharedWords++
+				}
+
+				if nSharedWords < minWords {
+					return
+				}
+
+				if pNSharedWords == 0 { // 2nd sequence
+					pNSharedWords = nSharedWords
+				} else if nSharedWords != pNSharedWords { // number of shared words changed
+					return
+				}
+
+				record.FormatToWriter(outfh, config.LineWidth)
+			}
+
+			config.LineWidth = lineWidth
+		}
+	},
+}
+
+func init() {
+	RootCmd.AddCommand(headGenomeCmd)
+
+	headGenomeCmd.Flags().IntP("mini-common-words", "m", 1, "minimal shared prefix words")
+}


=====================================
seqkit/cmd/locate.go
=====================================
@@ -26,6 +26,7 @@ import (
 	"io"
 	"regexp"
 	"runtime"
+	"sync"
 
 	"github.com/shenwei356/bio/seq"
 	"github.com/shenwei356/bio/seqio/fastx"
@@ -33,6 +34,7 @@ import (
 	"github.com/shenwei356/bwt/fmi"
 	"github.com/shenwei356/xopen"
 	"github.com/spf13/cobra"
+	"github.com/twotwotwo/sorts/sortutil"
 )
 
 // locateCmd represents the locate command
@@ -54,8 +56,7 @@ Attentions:
      parser accepts comma-separated-values (CSV) for multiple values (motifs).
      Patterns in file do not follow this rule.     
   4. Mismatch is allowed using flag "-m/--max-mismatch",
-     but it's not fast enough for large genome like human genome.
-     Though, it's fast enough for microbial genomes.
+     you can increase the value of "-j/--threads" to accelerate processing.
   5. When using flag --circular, end position of matched subsequence that 
      crossing genome sequence end would be greater than sequence length.
 
@@ -92,6 +93,8 @@ Attentions:
 		hideMatched := getFlagBool(cmd, "hide-matched")
 		circular := getFlagBool(cmd, "circular")
 
+		immediateOutput := getFlagBool(cmd, "immediate-output")
+
 		if config.Alphabet == seq.Protein {
 			onlyPositiveStrand = true
 		}
@@ -100,7 +103,6 @@ Attentions:
 			checkError(fmt.Errorf("one of flags -p (--pattern) and -f (--pattern-file) needed"))
 		}
 
-		var sfmi *fmi.FMIndex
 		if mismatches > 0 {
 			if degenerate {
 				checkError(fmt.Errorf("flag -d (--degenerate) not allowed when giving flag -m (--max-mismatch)"))
@@ -111,7 +113,7 @@ Attentions:
 			if nonGreedy && !quiet {
 				log.Infof("flag -G (--non-greedy) ignored when giving flag -m (--max-mismatch)")
 			}
-			sfmi = fmi.NewFMIndex()
+
 		}
 		if useFMI {
 			if degenerate {
@@ -120,7 +122,6 @@ Attentions:
 			if useRegexp {
 				checkError(fmt.Errorf("flag -r (--use-regexp) ignored when giving flag -F (--use-fmi)"))
 			}
-			sfmi = fmi.NewFMIndex()
 		}
 
 		// prepare pattern
@@ -216,7 +217,6 @@ Attentions:
 						}
 						re, err := regexp.Compile(s)
 						checkError(err)
-						fmt.Println(s, re)
 						regexps[p] = re
 					} else if bytes.Index(patterns[p], []byte(".")) >= 0 ||
 						!(seq.DNAredundant.IsValid(patterns[p]) == nil ||
@@ -239,20 +239,309 @@ Attentions:
 				outfh.WriteString("seqID\tpatternName\tpattern\tstrand\tstart\tend\tmatched\n")
 			}
 		}
+
+		// -------------------------------------------------------------------
+		// only for m > 0, where FMI is slow
+
+		var record *fastx.Record
+		var fastxReader *fastx.Reader
+		_onlyPositiveStrand := onlyPositiveStrand
+
+		if mismatches > 0 || useFMI {
+			type Arecord struct {
+				id     uint64
+				ok     bool
+				record []string
+			}
+
+			var wg sync.WaitGroup
+			ch := make(chan *Arecord, config.Threads)
+			tokens := make(chan int, config.Threads)
+
+			done := make(chan int)
+			go func() {
+				m := make(map[uint64]*Arecord, config.Threads)
+				var id, _id uint64
+				var ok bool
+				var _r *Arecord
+				var row string
+
+				id = 1
+				for r := range ch {
+					_id = r.id
+
+					if _id == id { // right there
+						if r.ok {
+							for _, row = range r.record {
+								outfh.WriteString(row)
+							}
+
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+						id++
+						continue
+					}
+
+					m[_id] = r // save for later check
+
+					if _r, ok = m[id]; ok { // check buffered
+						if _r.ok {
+							for _, row = range _r.record {
+								outfh.WriteString(row)
+							}
+
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+						delete(m, id)
+						id++
+					}
+				}
+
+				if len(m) > 0 {
+					ids := make([]uint64, len(m))
+					i := 0
+					for _id = range m {
+						ids[i] = _id
+						i++
+					}
+					sortutil.Uint64s(ids)
+					for _, _id = range ids {
+						_r = m[_id]
+
+						if _r.ok {
+							for _, row = range _r.record {
+								outfh.WriteString(row)
+							}
+
+							if immediateOutput {
+								outfh.Flush()
+							}
+						}
+					}
+				}
+				done <- 1
+			}()
+
+			var id uint64
+			for _, file := range files {
+				fastxReader, err = fastx.NewReader(alphabet, file, idRegexp)
+				checkError(err)
+
+				checkAlphabet := true
+				for {
+					record, err = fastxReader.Read()
+					if err != nil {
+						if err == io.EOF {
+							break
+						}
+						checkError(err)
+						break
+					}
+
+					if checkAlphabet {
+						if fastxReader.Alphabet() == seq.Unlimit || fastxReader.Alphabet() == seq.Protein {
+							_onlyPositiveStrand = true
+						}
+						checkAlphabet = false
+					}
+
+					tokens <- 1
+					wg.Add(1)
+					id++
+					go func(record *fastx.Record, id uint64) {
+						defer func() {
+							wg.Done()
+							<-tokens
+						}()
+
+						var seqRP *seq.Seq
+						var l int
+						var loc []int
+						var i, begin, end int
+						var pSeq []byte
+						var pName string
+						var sfmi *fmi.FMIndex
+						sfmi = fmi.NewFMIndex()
+						results := make([]string, 0, 2)
+
+						if !(degenerate || useRegexp) && ignoreCase {
+							record.Seq.Seq = bytes.ToLower(record.Seq.Seq)
+						}
+
+						l = len(record.Seq.Seq)
+
+						if circular { // concat two copies of sequence
+							record.Seq.Seq = append(record.Seq.Seq, record.Seq.Seq...)
+						}
+
+						_, err = sfmi.Transform(record.Seq.Seq)
+						if err != nil {
+							checkError(fmt.Errorf("fail to build FMIndex for sequence: %s", record.Name))
+						}
+
+						for pName, pSeq = range patterns {
+							loc, err = sfmi.Locate(pSeq, mismatches)
+							if err != nil {
+								checkError(fmt.Errorf("fail to search pattern '%s' on seq '%s': %s", pName, record.Name, err))
+							}
+							for _, i = range loc {
+								if circular && i+1 > l { // 2nd clone of original part
+									continue
+								}
+
+								begin = i + 1
+
+								end = i + len(pSeq)
+								if i+len(pSeq) > len(record.Seq.Seq) {
+									continue
+								}
+								if outFmtGTF {
+									results = append(results, fmt.Sprintf("%s\t%s\t%s\t%d\t%d\t%d\t%s\t%s\tgene_id \"%s\"; \n",
+										record.ID,
+										"SeqKit",
+										"location",
+										begin,
+										end,
+										0,
+										"+",
+										".",
+										pName))
+								} else if outFmtBED {
+									results = append(results, fmt.Sprintf("%s\t%d\t%d\t%s\t%d\t%s\n",
+										record.ID,
+										begin-1,
+										end,
+										pName,
+										0,
+										"+"))
+								} else {
+									if hideMatched {
+										results = append(results, fmt.Sprintf("%s\t%s\t%s\t%s\t%d\t%d\n",
+											record.ID,
+											pName,
+											patterns[pName],
+											"+",
+											begin,
+											end))
+									} else {
+										results = append(results, fmt.Sprintf("%s\t%s\t%s\t%s\t%d\t%d\t%s\n",
+											record.ID,
+											pName,
+											patterns[pName],
+											"+",
+											begin,
+											end,
+											record.Seq.Seq[i:i+len(pSeq)]))
+									}
+								}
+							}
+						}
+
+						if _onlyPositiveStrand {
+							ch <- &Arecord{record: results, id: id, ok: len(results) > 0}
+							return
+						}
+
+						seqRP = record.Seq.RevCom()
+
+						_, err = sfmi.Transform(seqRP.Seq)
+						if err != nil {
+							checkError(fmt.Errorf("fail to build FMIndex for reverse complement sequence: %s", record.Name))
+						}
+						for pName, pSeq = range patterns {
+							loc, err = sfmi.Locate(pSeq, mismatches)
+							if err != nil {
+								checkError(fmt.Errorf("fail to search pattern '%s' on seq '%s': %s", pName, record.Name, err))
+							}
+							for _, i = range loc {
+								if circular && i+1 > l { // 2nd clone of original part
+									continue
+								}
+
+								begin = l - i - len(pSeq) + 1
+								end = l - i
+								if i+len(pSeq) > len(record.Seq.Seq) {
+									continue
+								}
+								if outFmtGTF {
+									results = append(results, fmt.Sprintf("%s\t%s\t%s\t%d\t%d\t%d\t%s\t%s\tgene_id \"%s\"; \n",
+										record.ID,
+										"SeqKit",
+										"location",
+										begin,
+										end,
+										0,
+										"-",
+										".",
+										pName))
+								} else if outFmtBED {
+									results = append(results, fmt.Sprintf("%s\t%d\t%d\t%s\t%d\t%s\n",
+										record.ID,
+										begin-1,
+										end,
+										pName,
+										0,
+										"-"))
+								} else {
+									if hideMatched {
+										results = append(results, fmt.Sprintf("%s\t%s\t%s\t%s\t%d\t%d\n",
+											record.ID,
+											pName,
+											patterns[pName],
+											"-",
+											begin,
+											end))
+									} else {
+										results = append(results, fmt.Sprintf("%s\t%s\t%s\t%s\t%d\t%d\t%s\n",
+											record.ID,
+											pName,
+											patterns[pName],
+											"-",
+											begin,
+											end,
+											seqRP.Seq[i:i+len(pSeq)]))
+									}
+								}
+							}
+						}
+
+						ch <- &Arecord{record: results, id: id, ok: len(results) > 0}
+					}(record.Clone(), id)
+				}
+			}
+
+			wg.Wait()
+			close(ch)
+			<-done
+
+			return
+		}
+
+		// -------------------------------------------------------------------
+
 		var seqRP *seq.Seq
 		var offset, l, lpatten int
 		var loc []int
 		var locs, locsNeg [][2]int
 		var i, begin, end int
 		var flag bool
-		var record *fastx.Record
-		var fastxReader *fastx.Reader
 		var pSeq, p []byte
 		var pName string
 		var re *regexp.Regexp
+		var sfmi *fmi.FMIndex
+		if mismatches > 0 || useFMI {
+			sfmi = fmi.NewFMIndex()
+		}
+
 		for _, file := range files {
 			fastxReader, err = fastx.NewReader(alphabet, file, idRegexp)
 			checkError(err)
+
+			checkAlphabet := true
 			for {
 				record, err = fastxReader.Read()
 				if err != nil {
@@ -263,6 +552,13 @@ Attentions:
 					break
 				}
 
+				if checkAlphabet {
+					if fastxReader.Alphabet() == seq.Unlimit || fastxReader.Alphabet() == seq.Protein {
+						_onlyPositiveStrand = true
+					}
+					checkAlphabet = false
+				}
+
 				if !(degenerate || useRegexp) && ignoreCase {
 					record.Seq.Seq = bytes.ToLower(record.Seq.Seq)
 				}
@@ -273,10 +569,6 @@ Attentions:
 					record.Seq.Seq = append(record.Seq.Seq, record.Seq.Seq...)
 				}
 
-				if !onlyPositiveStrand {
-					seqRP = record.Seq.RevCom()
-				}
-
 				if mismatches > 0 || useFMI {
 					_, err = sfmi.Transform(record.Seq.Seq)
 					if err != nil {
@@ -341,10 +633,12 @@ Attentions:
 						}
 					}
 
-					if onlyPositiveStrand {
+					if _onlyPositiveStrand {
 						continue
 					}
 
+					seqRP = record.Seq.RevCom()
+
 					_, err = sfmi.Transform(seqRP.Seq)
 					if err != nil {
 						checkError(fmt.Errorf("fail to build FMIndex for reverse complement sequence: %s", record.Name))
@@ -406,6 +700,10 @@ Attentions:
 						}
 					}
 
+					if immediateOutput {
+						outfh.Flush()
+					}
+
 					continue
 				}
 
@@ -507,6 +805,8 @@ Attentions:
 						continue
 					}
 
+					seqRP = record.Seq.RevCom()
+
 					locsNeg = make([][2]int, 0, 1000)
 
 					offset = 0
@@ -600,6 +900,10 @@ Attentions:
 						}
 					}
 				}
+
+				if immediateOutput {
+					outfh.Flush()
+				}
 			}
 		}
 	},
@@ -622,4 +926,5 @@ func init() {
 	locateCmd.Flags().IntP("max-mismatch", "m", 0, "max mismatch when matching by seq. For large genomes like human genome, using mapping/alignment tools would be faster")
 	locateCmd.Flags().BoolP("hide-matched", "M", false, "do not show matched sequences")
 	locateCmd.Flags().BoolP("circular", "c", false, `circular genome. type "seqkit locate -h" for details`)
+	locateCmd.Flags().BoolP("immediate-output", "I", false, "print output immediately, do not use write buffer")
 }


=====================================
seqkit/cmd/tab2fx.go
=====================================
@@ -21,12 +21,11 @@
 package cmd
 
 import (
-	"bytes"
+	"bufio"
 	"fmt"
 	"runtime"
 	"strings"
 
-	"github.com/shenwei356/breader"
 	"github.com/shenwei356/util/byteutil"
 	"github.com/shenwei356/xopen"
 	"github.com/spf13/cobra"
@@ -53,92 +52,57 @@ var tab2faCmd = &cobra.Command{
 		checkError(err)
 		defer outfh.Close()
 
-		type Slice []string
-		fn := func(line string) (interface{}, bool, error) {
-			line = strings.TrimRight(line, "\r\n")
-
-			if line == "" {
-				return "", false, nil
-			}
-			// check comment line
-			isCommentLine := false
-			for _, p := range commentPrefixes {
-				if strings.HasPrefix(line, p) {
-					isCommentLine = true
-					break
-				}
-			}
-			if isCommentLine {
-				return "", false, nil
-			}
-
-			items := strings.Split(line, "\t")
-			if len(items) < 2 {
-				return Slice(items), false, fmt.Errorf("at least two columns needed: %s", line)
-			}
-			if len(items) > 2 {
-				return Slice(items[0:3]), true, nil
-			}
-			return Slice(items[0:2]), true, nil
-		}
-
+		var line, p string
+		var items []string
+		var isCommentLine, isFastq bool
+		var scanner *bufio.Scanner
+		var fh *xopen.Reader
 		for _, file := range files {
-			reader, err := breader.NewBufferedReader(file, config.Threads, 10, fn)
+			fh, err = xopen.Ropen(file)
 			checkError(err)
-			var text []byte
-			var b *bytes.Buffer
-			isFastq := false
-			for chunk := range reader.Ch {
-				if chunk.Err != nil {
-					checkError(chunk.Err)
-					break
-				}
-				for _, data := range chunk.Data {
-					items := data.(Slice)
-					if len(items) == 3 && (len(items[2]) > 0 || isFastq) { // fastq
-						isFastq = true
-						outfh.WriteString(fmt.Sprintf("@%s\n", items[0]))
-
-						// 	outfh.Write(byteutil.WrapByteSlice([]byte(items[1]), lineWidth))
 
-						// if bufferedByteSliceWrapper == nil {
-						// 	bufferedByteSliceWrapper = byteutil.NewBufferedByteSliceWrapper2(1, len(items[1]), lineWidth)
-						// }
-						// text, b = bufferedByteSliceWrapper.Wrap([]byte(items[1]), lineWidth)
-						// outfh.Write(text)
-						// outfh.Flush()
-						// bufferedByteSliceWrapper.Recycle(b)
+			scanner = bufio.NewScanner(fh)
 
-						outfh.WriteString(items[1]) // seq
+			isFastq = false
+			for scanner.Scan() {
+				line = strings.TrimRight(scanner.Text(), "\r\n")
 
-						outfh.WriteString("\n+\n")
-
-						// outfh.Write(byteutil.WrapByteSlice([]byte(items[2]), lineWidth))
-
-						// text, b = bufferedByteSliceWrapper.Wrap([]byte(items[2]), lineWidth)
-						// outfh.Write(text)
-						// outfh.Flush()
-						// bufferedByteSliceWrapper.Recycle(b)
-
-						outfh.WriteString(items[2]) // qual
-
-						outfh.WriteString("\n")
-					} else {
-						outfh.WriteString(fmt.Sprintf(">%s\n", items[0]))
+				if line == "" {
+					continue
+				}
+				// check comment line
+				isCommentLine = false
+				for _, p = range commentPrefixes {
+					if strings.HasPrefix(line, p) {
+						isCommentLine = true
+						break
+					}
+				}
+				if isCommentLine {
+					continue
+				}
 
-						// outfh.Write(byteutil.WrapByteSlice([]byte(items[1]), lineWidth))
-						if bufferedByteSliceWrapper == nil {
-							bufferedByteSliceWrapper = byteutil.NewBufferedByteSliceWrapper2(1, len(items[1]), lineWidth)
-						}
-						text, b = bufferedByteSliceWrapper.Wrap([]byte(items[1]), lineWidth)
-						outfh.Write(text)
-						outfh.Flush()
-						bufferedByteSliceWrapper.Recycle(b)
+				items = strings.Split(line, "\t")
+				if len(items) < 2 {
+					checkError(fmt.Errorf("at least two columns needed: %s", line))
+				}
 
-						outfh.WriteString("\n")
-					}
+				if len(items) == 3 && (len(items[2]) > 0 || isFastq) { // fastq
+					isFastq = true
+					outfh.WriteString(fmt.Sprintf("@%s\n", items[0]))
+					outfh.WriteString(items[1]) // seq
+					outfh.WriteString("\n+\n")
+					outfh.WriteString(items[2]) // qual
+
+					outfh.WriteString("\n")
+				} else {
+					outfh.WriteString(fmt.Sprintf(">%s\n", items[0]))
+					outfh.Write(byteutil.WrapByteSlice([]byte(items[1]), lineWidth))
+					outfh.WriteString("\n")
 				}
+
 			}
+			checkError(scanner.Err())
 		}
 	},
 }


=====================================
seqkit/cmd/version.go
=====================================
@@ -29,7 +29,7 @@ import (
 )
 
 // VERSION of seqkit
-const VERSION = "0.15.0"
+const VERSION = "0.16.0"
 
 // versionCmd represents the version command
 var versionCmd = &cobra.Command{



View it on GitLab: https://salsa.debian.org/med-team/seqkit/-/compare/3319904bcfcf11e1aec52a0768ab32fb176320b6...35416836997cb0df1155b11c803ea93aee2267c6

-- 
View it on GitLab: https://salsa.debian.org/med-team/seqkit/-/compare/3319904bcfcf11e1aec52a0768ab32fb176320b6...35416836997cb0df1155b11c803ea93aee2267c6
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20210501/9672971d/attachment-0001.htm>


More information about the debian-med-commit mailing list