diff --git a/autoload/SpaceVim/layers/chinese.vim b/autoload/SpaceVim/layers/chinese.vim index 8bbde9cec..cf708d257 100644 --- a/autoload/SpaceVim/layers/chinese.vim +++ b/autoload/SpaceVim/layers/chinese.vim @@ -32,8 +32,8 @@ function! SpaceVim#layers#chinese#plugins() abort \ ['yianwillis/vimcdoc' , {'merged' : 0}], \ ['NamelessUzer/Vim-Natural-Language-Number-Translator' , {'merged' : 0}], \ ['voldikss/vim-translator' , {'merged' : 0, 'on_cmd' : ['Translate', 'TranslateW', 'TranslateR', 'TranslateX']}], - \ ['wsdjeg/ChineseLinter.vim' , {'merged' : 0, 'on_cmd' : 'CheckChinese', 'on_ft' : ['markdown', 'text']}], \ ] + call add(plugins, [g:_spacevim_root_dir . 'bundle/ChineseLinter.vim' , {'merged' : 0, 'on_cmd' : 'CheckChinese', 'on_ft' : ['markdown', 'text']}]) if SpaceVim#layers#isLoaded('ctrlp') call add(plugins, ['vimcn/ctrlp.cnx', {'merged' : 0}]) endif diff --git a/bundle/ChineseLinter.vim/.ci/install.sh b/bundle/ChineseLinter.vim/.ci/install.sh new file mode 100644 index 000000000..375ce12d6 --- /dev/null +++ b/bundle/ChineseLinter.vim/.ci/install.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Fail on unset variables and command errors +set -ue -o pipefail + +# Prevent commands misbehaving due to locale differences +export LC_ALL=C + +pip install covimerage +git clone --depth=1 https://github.com/Shougo/dein.vim.git ~/.cache/vimfiles/repos/github.com/Shougo/dein.vim +if [[ ! -d "${DEPS}/_neovim" ]]; then + mkdir -p "${DEPS}/_neovim" + wget -q -O - https://github.com/neovim/neovim/releases/download/nightly/nvim-${TRAVIS_OS_NAME}64.tar.gz \ + | tar xzf - --strip-components=1 -C "${DEPS}/_neovim" + +fi +export PATH="${DEPS}/_neovim/bin:${PATH}" +echo "\$PATH: \"${PATH}\"" + +export VIM="${DEPS}/_neovim/share/nvim/runtime" +echo "\$VIM: \"${VIM}\"" +nvim --version diff --git a/bundle/ChineseLinter.vim/.ci/script.sh b/bundle/ChineseLinter.vim/.ci/script.sh new file mode 100644 index 000000000..55291038f --- /dev/null +++ b/bundle/ChineseLinter.vim/.ci/script.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -ex +export PATH="${DEPS}/_neovim/bin:${PATH}" +echo "\$PATH: \"${PATH}\"" + +export VIM="${DEPS}/_neovim/share/nvim/runtime" +nvim --version +make test_coverage +covimerage -vv xml --omit 'build/*' +pip install codecov +codecov -X search gcov pycov -f coverage.xml +set +x diff --git a/bundle/ChineseLinter.vim/.gitignore b/bundle/ChineseLinter.vim/.gitignore new file mode 100644 index 000000000..567609b12 --- /dev/null +++ b/bundle/ChineseLinter.vim/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/bundle/ChineseLinter.vim/.travis.yml b/bundle/ChineseLinter.vim/.travis.yml new file mode 100644 index 000000000..94f8445db --- /dev/null +++ b/bundle/ChineseLinter.vim/.travis.yml @@ -0,0 +1,34 @@ +language: python +dist: trusty +sudo: false + +services: + - docker + +git: + depth: 3 + +notifications: + slack: + on_success: never + on_failure: never + email: + on_success: never # default: change + on_failure: never # default: always +cache: + bundler: true + directories: + - $HOME/cache/ + - $HOME/deps + +env: + global: + - DEPS=$HOME/deps + - PATH=$DEPS/bin:$HOME/vim/bin:$PATH + +jobs: + include: + - env: LINT=vader + +install: .ci/install.sh +script: .ci/script.sh diff --git a/bundle/ChineseLinter.vim/Makefile b/bundle/ChineseLinter.vim/Makefile new file mode 100644 index 000000000..6ac55bdc7 --- /dev/null +++ b/bundle/ChineseLinter.vim/Makefile @@ -0,0 +1,26 @@ +test: build/vader | build + nvim -Nu test/vimrc -c 'Vader! test/**' + +test_show: build/vader | build + nvim -Nu test/vimrc -c 'Vader test/**' + +COVIMERAGE=$(shell command -v covimerage 2>/dev/null || echo build/covimerage/bin/covimerage) + +test_coverage: $(COVIMERAGE) build/vader | build + $(COVIMERAGE) run nvim -Nu test/vimrc -c 'Vader! test/**' + +build/covimerage: + virtualenv $@ +build/covimerage/bin/covimerage: | build/covimerage + build/covimerage/bin/pip install covimerage + +build/vader: + git clone --depth 1 https://github.com/junegunn/vader.vim.git $@ + +build: + mkdir -p $@ + +clean: + $(RM) -r build + +.PHONY: clean test diff --git a/bundle/ChineseLinter.vim/README.md b/bundle/ChineseLinter.vim/README.md new file mode 100644 index 000000000..cb9cf2b30 --- /dev/null +++ b/bundle/ChineseLinter.vim/README.md @@ -0,0 +1,48 @@ +# ChineseLinter.vim + +> 中文文档语言规范检查工具 + +[![Build Status](https://travis-ci.org/wsdjeg/ChineseLinter.vim.svg?branch=master)](https://travis-ci.org/wsdjeg/ChineseLinter.vim) +[![codecov](https://codecov.io/gh/wsdjeg/ChineseLinter.vim/branch/master/graph/badge.svg)](https://codecov.io/gh/wsdjeg/ChineseLinter.vim) + +## 使用说明 + +在编辑中文文档时,使用如下命令即可检查,错误信息将被展示在 `local list` 窗口。 + +```vim +:CheckChinese +``` + +## 错误代码 + +| 代码 | 描述 | +| ------ | ------------------------------- | +| `E001` | 中文字符后存在英文标点 | +| `E002` | 中英文之间没有空格 | +| `E003` | 中文与数字之间没有空格 | +| `E004` | 中文标点两侧存在空格 | +| `E005` | 行尾含有空格 | +| `E006` | 数字和单位之间存在空格 | +| `E007` | 数字使用了全角字符 | +| `E008` | 汉字之间存在空格 | +| `E009` | 中文标点重复 | +| `E010` | 英文标点符号两侧的空格数量不对 | +| `E011` | 中英文之间空格数量多于 1 个 | +| `E012` | 中文和数字之间空格数量多于 1 个 | +| `E013` | 英文和数字之间没有空格 | +| `E014` | 英文和数字之间空格数量多于 1 个 | +| `E015` | 英文标点重复 | +| `E016` | 连续的空行数量大于 2 行 | +| `E017` | 数字之间存在空格 | + +## 配置 + +如果需要忽略某些错误,可以将错误代码加入选项:`g:chinese_linter_disabled_nr` + +```vim +let g:chinese_linter_disabled_nr = ['E002', 'E005'] +``` + +## 参考指南: + +- [中文文案排版指北(简体中文版)](https://github.com/mzlogin/chinese-copywriting-guidelines) diff --git a/bundle/ChineseLinter.vim/addon-info.json b/bundle/ChineseLinter.vim/addon-info.json new file mode 100644 index 000000000..f9e9d54a4 --- /dev/null +++ b/bundle/ChineseLinter.vim/addon-info.json @@ -0,0 +1,5 @@ +{ + "name": "ChineseLinter", + "description": "The Linter for Chinese", + "author": "wsdjeg" +} diff --git a/bundle/ChineseLinter.vim/autoload/ChineseLinter.vim b/bundle/ChineseLinter.vim/autoload/ChineseLinter.vim new file mode 100644 index 000000000..2938cb5ea --- /dev/null +++ b/bundle/ChineseLinter.vim/autoload/ChineseLinter.vim @@ -0,0 +1,215 @@ +scriptencoding utf-8 + +"" +" 指定需要忽略的错误、警告的编号,默认没有禁止。 +" > +" let g:chinese_linter_disabled_nr = ['E001'] +" < +" +" 目前支持的检查包括: +" > +" E001 | 中文字符后存在英文标点 +" E002 | 中英文之间没有空格 +" E003 | 中文和数字之间没有空格 +" E004 | 中文标点两侧存在空格 +" E005 | 行尾含有空格 +" E006 | 数字和单位之间存在空格 +" E007 | 数字使用了全角字符 +" E008 | 汉字之间存在空格 +" E009 | 中文标点重复 +" E010 | 英文标点符号两侧的空格数量不对 +" E011 | 中英文之间空格数量多于 1 个 +" E012 | 中文和数字之间空格数量多于 1 个 +" E013 | 英文和数字之间没有空格 +" E014 | 英文和数字之间空格数量多于 1 个 +" E015 | 英文标点重复 +" E016 | 连续的空行数量大于 2 行 +" E017 | 数字之间存在空格 +" < + +let g:chinese_linter_disabled_nr = get(g:,'chinese_linter_disabled_nr', []) + +"" +" This setting will open the |location-list| or |quickfix| list (depending on +" whether it is operating on a file) when adding entries. A value of 2 will +" preserve the cursor position when the |location-list| or |quickfix| window is +" opened. Defaults to 2. +let g:chinese_linter_open_list = 2 + +" 中文标点符号(更全) +" let s:CHINESE_PUNCTUATION = '[\u2014\u2015\u2018\u2019\u201c\u201d\u2026\u3001\u3002\u3008\u3009\u300a\u300b\u300c\u300d\u300e\u300f\u3010\u3011\u3014\u3015\ufe43\ufe44\ufe4f\uff01\uff08\uff09\uff0c\uff1a\uff1b\uff1f\uff5e\uffe5]' +" [\u2010-\u201f] == [‐‑‒–—―‖‗‘’‚‛“”„‟] +" [\u2026] == […] +" [\uff01-\uff0f] == [!"#$%&'()*+,-./] +" [\uff1a-\uff1f] == [:;<=>?] +" [\uff3b-\uff40] == [[\]^_`] +" [\uff5b-\uff5e] == [{|}~] +let s:CHINESE_PUNCTUATION = '[\u2010-\u201f\u2026\uff01-\uff0f\uff1a-\uff1f\uff3b-\uff40\uff5b-\uff5e]' + +" 英文标点 +let s:punctuation_en = '[,:;?!-]' + +" 中文标点符号 +" let s:punctuation_cn = '[‘’“”、。《》『』!"'(),/:;<=>?[]{}]' 与下面这行代码等价 +let s:punctuation_cn = '[\u2018\u2019\u201c\u201d\u3001\u3002\u300a\u300b\u300e\u300f\uff01\uff02\uff07\uff08\uff09\uff0c\uff0f\uff1a\uff1b\uff1c\uff1d\uff1e\uff1f\uff3b\uff3d\uff5b\uff5d]' + +" 中文汉字 +let s:chars_cn = '[\u4e00-\u9fff]' + +" 数字 +let s:numbers = '[0-9]' + +" 全角数字 +let s:numbers_cn = '[\uff10-\uff19]' + +" 英文字母 +let s:chars_en = '[a-zA-Z]' + +" 单位 +" TODO: 需要添加更多的单位,单位见以下链接 +" https://unicode-table.com/cn/blocks/cjk-compatibility/ +" https://unicode-table.com/cn/#2031 +" https://unicode-table.com/cn/#2100 +let s:symbol = '[%‰‱\u3371-\u33df\u2100-\u2109]' + +" 空白符号 +let s:blank = '\(\s\|[\u3000]\)' + +let s:ERRORS = { + \ 'E001' : [ + \ ['中文字符后存在英文标点' , s:chars_cn . s:blank . '*' . s:punctuation_en], + \ ], + \ 'E002' : [ + \ ['中文与英文之间没有空格' , s:chars_cn . s:chars_en], + \ ['英文与中文之间没有空格' , s:chars_en . s:chars_cn], + \ ], + \ 'E003' : [ + \ ['中文与数字之间没有空格' , s:chars_cn . s:numbers], + \ ['数字与中文之间没有空格' , s:numbers . s:chars_cn], + \ ], + \ 'E004' : [ + \ ['中文标点前存在空格' , s:blank . '\+\ze' . s:CHINESE_PUNCTUATION], + \ ['中文标点后存在空格' , s:CHINESE_PUNCTUATION . '\zs' . s:blank . '\+'], + \ ], + \ 'E005' : [ + \ ['行尾有空格' , s:blank . '\+$'], + \ ], + \ 'E006' : [ + \ ['数字和单位之间有空格' , s:numbers . '\zs' . s:blank . '\+\ze' . s:symbol], + \ ], + \ 'E007' : [ + \ ['数字使用了全角数字' , s:numbers_cn . '\+'], + \ ], + \ 'E008' : [ + \ ['汉字之间存在空格' , s:chars_cn . '\zs' . s:blank . '\+\ze' . s:chars_cn], + \ ], + \ 'E009' : [ + \ ['中文标点符号重复' , '\(' . s:punctuation_cn . '\)\1\+'], + \ ['连续多个中文标点符号' , '[、,:;。!?]\{2,}'], + \ ], + \ 'E010' : [ + \ ['英文标点前侧存在空格' , s:blank . '\+\ze' . s:punctuation_en], + \ ['英文标点符号后侧的空格数量多于 1 个' , s:punctuation_en . '\zs' . s:blank . '\{2,}'], + \ ['英文标点与英文之间没有空格' , '[,:;?!]' . s:chars_en], + \ ['英文标点与中文之间没有空格' , '[,:;?!]' . s:chars_cn], + \ ['英文标点与数字之间没有空格' , '[,:;?!]' . s:numbers], + \ ], + \ 'E011' : [ + \ ['中文与英文之间空格数量多于 1 个' , '\%#=2' . s:chars_cn . '\zs' . s:blank . '\{2,}\ze' . s:chars_en], + \ ['英文与中文之间空格数量多于 1 个' , '\%#=2' . s:chars_en . '\zs' . s:blank . '\{2,}\ze' . s:chars_cn], + \ ], + \ 'E012' : [ + \ ['中文与数字之间空格数量多于 1 个' , '\%#=2' . s:chars_cn . '\zs' . s:blank . '\{2,}\ze' . s:numbers], + \ ['数字与中文之间空格数量多于 1 个' , '\%#=2' . s:numbers . '\zs' . s:blank . '\{2,}\ze' . s:chars_cn], + \ ], + \ 'E013' : [ + \ ['英文与数字之间没有空格' , s:chars_en . s:numbers], + \ ['数字与英文之间没有空格' , s:numbers . s:chars_en], + \ ], + \ 'E014' : [ + \ ['英文与数字之间空格数量多于 1 个' , s:chars_en . '\zs' . s:blank . '\{2,}\ze' . s:numbers], + \ ['数字与英文之间空格数量多于 1 个' , s:numbers . '\zs' . s:blank . '\{2,}\ze' . s:chars_en], + \ ], + \ 'E015' : [ + \ ['英文标点符号重复' , '\(' . s:punctuation_en . s:blank . '*\)\1\+'], + \ ['连续多个英文标点符号' , '\(' . '[,:;?!-]' . s:blank . '*\)\{2,}'], + \ ], + \ 'E016' : [ + \ ['连续的空行数量大于 2 行' , '^\(' . s:blank . '*\n\)\{3,}'], + \ ], + \ 'E017' : [ + \ ['数字之间存在空格' , s:numbers . '\zs' . s:blank . '\+\ze' . s:numbers], + \ ], + \ } + +function! s:getNotIgnoreErrors() + let s:notIgnoreErrorList = [] + for l:errors_nr in keys(s:ERRORS) + if index(g:chinese_linter_disabled_nr, l:errors_nr) == -1 + call add(s:notIgnoreErrorList, l:errors_nr) + endif + endfor +endfunction + +function! ChineseLinter#check(...) abort + call s:getNotIgnoreErrors() + let s:file = getline(1, '$') + let s:bufnr = bufnr('%') + let s:linenr = 0 + let s:colnr = 0 + let s:qf = [] + for l:line in s:file + let s:linenr += 1 + call s:parser(l:line) + endfor + if !empty(s:qf) + call s:update_qf(s:qf) + if g:chinese_linter_open_list == 1 + rightbelow copen + elseif g:chinese_linter_open_list ==2 + rightbelow copen + wincmd p + endif + else + call setqflist([]) + cclose + doautocmd WinEnter + endif + unlet s:linenr + unlet s:colnr +endfunction + +function! s:parser(line) abort + for l:errors_nr in s:notIgnoreErrorList + call s:find_error(l:errors_nr, a:line) + endfor +endfunction + +function! s:find_error(errors_nr, line) abort + let l:errorList = s:ERRORS[a:errors_nr] + for l:error in l:errorList + let s:colnr = matchend(a:line, l:error[1]) + if s:colnr != -1 + call s:add_to_qf(a:errors_nr, l:error[0]) + endif + endfor +endfunction + +function! s:add_to_qf(errors_nr, errors_text) abort + let l:error_item = { + \ 'bufnr': s:bufnr, + \ 'lnum' : s:linenr, + \ 'col' : s:colnr, + \ 'vcol' : 0, + \ 'text' : a:errors_nr . ' ' . a:errors_text, + \ 'nr' : a:errors_nr, + \ 'type' : 'E', + \ } + call add(s:qf, l:error_item) +endfunction + +" TODO 加入语法分析 + +function! s:update_qf(listOfDicts) abort + call setqflist(a:listOfDicts) +endfunction diff --git a/bundle/ChineseLinter.vim/codecov.yml b/bundle/ChineseLinter.vim/codecov.yml new file mode 100644 index 000000000..b79497700 --- /dev/null +++ b/bundle/ChineseLinter.vim/codecov.yml @@ -0,0 +1,9 @@ +coverage: + range: 30..60 + round: down + precision: 2 +comment: + layout: "header, diff, changes, uncovered" + behavior: default # update if exists else create new +codecov: + branch: master diff --git a/bundle/ChineseLinter.vim/doc/ChineseLinter.txt b/bundle/ChineseLinter.vim/doc/ChineseLinter.txt new file mode 100644 index 000000000..adce08dd8 --- /dev/null +++ b/bundle/ChineseLinter.vim/doc/ChineseLinter.txt @@ -0,0 +1,26 @@ +*ChineseLinter.txt* The Linter for Chinese +wsdjeg *ChineseLinter* + +============================================================================== +CONTENTS *ChineseLinter-contents* + 1. Configuration......................................|ChineseLinter-config| + 2. Commands.........................................|ChineseLinter-commands| + +============================================================================== +CONFIGURATION *ChineseLinter-config* + + *g:chinese_linter_open_list* +This setting will open the |location-list| or |quickfix| list (depending on +whether it is operating on a file) when adding entries. A value of 2 will +preserve the cursor position when the |location-list| or |quickfix| window is +opened. Defaults to 2. + +============================================================================== +COMMANDS *ChineseLinter-commands* + +:CheckChinese *:CheckChinese* + Start checking the document, and the results will be shown in the local + list. + + +vim:tw=78:ts=8:ft=help:norl: diff --git a/bundle/ChineseLinter.vim/plugin/ChineseLinter.vim b/bundle/ChineseLinter.vim/plugin/ChineseLinter.vim new file mode 100644 index 000000000..879b30fba --- /dev/null +++ b/bundle/ChineseLinter.vim/plugin/ChineseLinter.vim @@ -0,0 +1,4 @@ +"" +" Start checking the document, and the results will be shown in the local +" list. +command! -nargs=? CheckChinese call ChineseLinter#check() diff --git a/bundle/ChineseLinter.vim/test/clear.vader b/bundle/ChineseLinter.vim/test/clear.vader new file mode 100644 index 000000000..baa7eece0 --- /dev/null +++ b/bundle/ChineseLinter.vim/test/clear.vader @@ -0,0 +1,12 @@ +Given markdown: + 你好,中文 +Execute (test clear errors): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 7 + AssertEqual getqflist()[0].text[:3], 'E001' + wincmd p + call setline(1, '你好,中文') + CheckChinese + AssertEqual len(getqflist()), 0 + diff --git a/bundle/ChineseLinter.vim/test/e001.vader b/bundle/ChineseLinter.vim/test/e001.vader new file mode 100644 index 000000000..1a11561e2 --- /dev/null +++ b/bundle/ChineseLinter.vim/test/e001.vader @@ -0,0 +1,7 @@ +Given markdown: + 你好,中文 +Execute (test error E001): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 7 + AssertEqual getqflist()[0].text[:3], 'E001' diff --git a/bundle/ChineseLinter.vim/test/e002.vader b/bundle/ChineseLinter.vim/test/e002.vader new file mode 100644 index 000000000..f3adb062f --- /dev/null +++ b/bundle/ChineseLinter.vim/test/e002.vader @@ -0,0 +1,8 @@ +Given markdown: + 你好hello +Execute (test error E002): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 7 + AssertEqual getqflist()[0].text[:3], 'E002' + diff --git a/bundle/ChineseLinter.vim/test/e003.vader b/bundle/ChineseLinter.vim/test/e003.vader new file mode 100644 index 000000000..8cb4d9f51 --- /dev/null +++ b/bundle/ChineseLinter.vim/test/e003.vader @@ -0,0 +1,7 @@ +Given markdown: + 你好123 +Execute (test error E001): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 7 + AssertEqual getqflist()[0].text[:3], 'E003' diff --git a/bundle/ChineseLinter.vim/test/e004.vader b/bundle/ChineseLinter.vim/test/e004.vader new file mode 100644 index 000000000..f2850100d --- /dev/null +++ b/bundle/ChineseLinter.vim/test/e004.vader @@ -0,0 +1,7 @@ +Given markdown: + 你好中文 +Execute (test error E005): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 13 + AssertEqual getqflist()[0].text[:3], 'E005' diff --git a/bundle/ChineseLinter.vim/test/e005.vader b/bundle/ChineseLinter.vim/test/e005.vader new file mode 100644 index 000000000..aafbc4a1a --- /dev/null +++ b/bundle/ChineseLinter.vim/test/e005.vader @@ -0,0 +1,7 @@ +Given markdown: + 你好, 中文 +Execute (test error E001): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 10 + AssertEqual getqflist()[0].text[:3], 'E004' diff --git a/bundle/ChineseLinter.vim/test/e006.vader b/bundle/ChineseLinter.vim/test/e006.vader new file mode 100644 index 000000000..2d1628b54 --- /dev/null +++ b/bundle/ChineseLinter.vim/test/e006.vader @@ -0,0 +1,7 @@ +Given markdown: + 100 % +Execute (test error E006): + CheckChinese + AssertEqual getqflist()[0].lnum, 1 + AssertEqual getqflist()[0].col, 5 + AssertEqual getqflist()[0].text[:3], 'E006' diff --git a/bundle/ChineseLinter.vim/test/vimrc b/bundle/ChineseLinter.vim/test/vimrc new file mode 100644 index 000000000..d316092f1 --- /dev/null +++ b/bundle/ChineseLinter.vim/test/vimrc @@ -0,0 +1,7 @@ +filetype off +set rtp+=build/vader +set rtp+=. +set rtp+=after +filetype plugin indent on +syntax enable +exe 'set nocompatible'