├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── README.md ├── package.json ├── scripts ├── gen_package_type.js ├── gendoc.ts └── readme.md ├── src ├── index.ts └── types │ └── pkg-config.d.ts ├── tsconfig.json └── webpack.config.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | }, 5 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 6 | parser: '@typescript-eslint/parser', 7 | parserOptions: { 8 | ecmaVersion: 2018, 9 | sourceType: 'module', 10 | }, 11 | plugins: ['@typescript-eslint'], 12 | rules: { 13 | '@typescript-eslint/explicit-function-return-type': 'off', 14 | '@typescript-eslint/no-explicit-any': 'off', 15 | '@typescript-eslint/no-non-null-assertion': 'off', 16 | '@typescript-eslint/no-empty-function': 'off', 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: push 4 | 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Setup Node 11 | uses: actions/setup-node@v1 12 | with: 13 | node-version: '12.x' 14 | registry-url: 'https://registry.npmjs.org' 15 | - name: Build Package 16 | run: | 17 | npm install -g json ts-node 18 | npm install 19 | - name: Publish 20 | env: 21 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 22 | run: | 23 | pkgname=$(json name < package.json) 24 | localversion=$(json version < package.json) 25 | remoteversion=$(npm view ${pkgname} version) 26 | 27 | echo ${localversion} 28 | echo ${remoteversion} 29 | 30 | if [ ${localversion} \> ${remoteversion} ] 31 | then 32 | npm publish 33 | git config user.name github-actions 34 | git config user.email github-actions@github.com 35 | git tag -a ${localversion} -m v${localversion} 36 | git push --tags 37 | fi 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # For current directory only 2 | # ---------------------------------------------------------------------------- 3 | /source 4 | /lib 5 | /package-lock.json 6 | # General 7 | # ---------------------------------------------------------------------------- 8 | *.o 9 | *.out 10 | 11 | # log 12 | *.log 13 | 14 | # cache 15 | *.cache 16 | cache/ 17 | 18 | # Windows 19 | # ---------------------------------------------------------------------------- 20 | Thumbs.db 21 | Desktop.ini 22 | 23 | # Tags 24 | # ----------------------------------------------------------------------------- 25 | TAGS 26 | !TAGS/ 27 | tags 28 | tags-cn 29 | !tags/ 30 | .tags 31 | .tags1 32 | tags.lock 33 | tags.temp 34 | gtags.files 35 | GTAGS 36 | GRTAGS 37 | GPATH 38 | cscope.files 39 | cscope.out 40 | cscope.in.out 41 | cscope.po.out 42 | 43 | # Vim 44 | # ------------------------------------------------------------------------------ 45 | [._]*.s[a-w][a-z] 46 | [._]s[a-w][a-z] 47 | *.un~ 48 | Session.vim 49 | .netrwhist 50 | *~ 51 | 52 | # Test % Tmp 53 | # ------------------------------------------------------------------------------- 54 | test.* 55 | tmp.* 56 | temp.* 57 | 58 | # Java 59 | # ------------------------------------------------------------------------------- 60 | *.class 61 | 62 | # JavaScript 63 | # ------------------------------------------------------------------------------- 64 | node_modules 65 | 66 | # Python 67 | # ------------------------------------------------------------------------------- 68 | *.pyc 69 | .idea/ 70 | /.idea 71 | build/ 72 | __pycache__ 73 | 74 | # Rust 75 | # ------------------------------------------------------------------------------- 76 | target/ 77 | **/*.rs.bk 78 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | node_modules 3 | tsconfig.json 4 | tslint.json 5 | yarn.lock 6 | webpack.config.js 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # coc-just-complete 2 | 3 | ![publish](https://github.com/voldikss/coc-just-complete/workflows/publish/badge.svg) 4 | [![npm version](https://badge.fury.io/js/coc-just-complete.svg)](https://badge.fury.io/js/coc-just-complete) 5 | 6 | Coc.nvim extension 7 | 8 | Press `_` to trigger buffer source completion in (neo)vim. 9 | 10 | ![](https://user-images.githubusercontent.com/20282795/105694302-c5224a00-5f3b-11eb-86b5-8a4e717aa676.gif) 11 | 12 | ## Installation 13 | 14 | ```vim 15 | :CocInstall coc-just-complete 16 | ``` 17 | 18 | ## Configuration 19 | 20 | 21 | 22 | Properties 23 |
24 | just-complete.shortcut: . 25 | Type:
string
Default:
"just-complete"
26 |
27 |
28 | just-complete.priority: . 29 | Type:
number
Default:
3
30 |
31 | 32 | 33 | 34 | ## Related 35 | 36 | - [coc-dot-complete](https://github.com/voldikss/coc-dot-complete) buffer source completion triggered by `.` 37 | - [coc-dash-complete](https://github.com/voldikss/coc-dash-complete) buffer source completion triggered by `-` 38 | 39 | 40 | 41 | ## License 42 | 43 | MIT 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coc-just-complete", 3 | "version": "0.0.3", 4 | "description": "One kind of buffer words completion", 5 | "main": "lib/index.js", 6 | "engines": { 7 | "coc": "^0.0.79" 8 | }, 9 | "keywords": [ 10 | "coc.nvim", 11 | "completion" 12 | ], 13 | "scripts": { 14 | "clean": "rimraf lib", 15 | "watch": "webpack --watch", 16 | "build:types": "node scripts/gen_package_type.js", 17 | "build:webpack": "webpack --mode production", 18 | "build:doc": "ts-node ./scripts/gendoc.ts", 19 | "build": "run-s build:types build:webpack build:doc", 20 | "prepare": "npx npm-run-all clean build", 21 | "lint": "eslint . --ext .ts" 22 | }, 23 | "activationEvents": [ 24 | "*" 25 | ], 26 | "repository": "https://github.com/voldikss/coc-just-complete", 27 | "homepage": "https://github.com/voldikss/coc-just-complete/#readme", 28 | "contributes": { 29 | "configuration": { 30 | "title": "JustComplete", 31 | "type": "object", 32 | "properties": { 33 | "just-complete.shortcut": { 34 | "type": "string", 35 | "default": "just-complete" 36 | }, 37 | "just-complete.priority": { 38 | "type": "number", 39 | "default": 3 40 | } 41 | } 42 | } 43 | }, 44 | "author": "dyzplus@gmail.com", 45 | "license": "MIT", 46 | "devDependencies": { 47 | "@types/node": "^14.14.16", 48 | "@typescript-eslint/eslint-plugin": "^4.11.1", 49 | "@typescript-eslint/parser": "^4.11.1", 50 | "@voldikss/tsconfig": "*", 51 | "coc.nvim": "^0.0.80", 52 | "eslint": "^7.16.0", 53 | "rimraf": "^3.0.2", 54 | "ts-loader": "^8.0.12", 55 | "typescript": "^4.1.3", 56 | "json-schema-to-typescript": "^10.1.2", 57 | "npm-run-all": "^4.1.5", 58 | "webpack": "^5.11.1", 59 | "webpack-cli": "^4.3.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /scripts/gen_package_type.js: -------------------------------------------------------------------------------- 1 | const Pkg = require('../package.json'); 2 | const fs = require('fs'); 3 | const { compile } = require('json-schema-to-typescript'); 4 | 5 | const fsp = fs.promises; 6 | 7 | async function main() { 8 | const s = await compile(Pkg.contributes.configuration, 'Extension', { 9 | style: { 10 | semi: true, 11 | singleQuote: true, 12 | }, 13 | }); 14 | await fsp.writeFile('src/types/pkg-config.d.ts', s); 15 | } 16 | 17 | main().then(console.error); 18 | 19 | -------------------------------------------------------------------------------- /scripts/gendoc.ts: -------------------------------------------------------------------------------- 1 | import Pkg from '../package.json'; 2 | import fs from 'fs'; 3 | import { JSONSchema7, JSONSchema7Type } from 'json-schema'; 4 | import ts from 'typescript'; 5 | import pathLib from 'path'; 6 | 7 | const fsp = fs.promises; 8 | 9 | type Definition = JSONSchema7; 10 | 11 | type Cmd = { 12 | title: string; 13 | command: string; 14 | }; 15 | 16 | type Section = { 17 | title?: string; 18 | rows: Row[]; 19 | }; 20 | 21 | type Row = { 22 | name: string; 23 | description: string; 24 | type?: string; 25 | default?: JSONSchema7Type; 26 | }; 27 | 28 | abstract class DocGenerator { 29 | protected ignorePrettierStart = ''; 30 | protected ignorePrettierEnd = ''; 31 | protected hint = ``; 32 | 33 | constructor(public generateCommand: string) {} 34 | 35 | abstract generate(): Promise; 36 | 37 | protected printJson(obj: JSONSchema7Type, format = false) { 38 | return JSON.stringify(obj, undefined, format ? ' ' : undefined); 39 | } 40 | 41 | protected printAsDetails(rows: Row[]) { 42 | const lines: string[] = []; 43 | rows.forEach((row) => { 44 | let hideLine = ''; 45 | if (row.type) { 46 | hideLine += `Type:
${row.type}
`; 47 | } 48 | if (row.default !== undefined) { 49 | hideLine += 'Default: '; 50 | hideLine += 51 | '
' + this.printJson(row.default, true) + '
'; 52 | } 53 | if (hideLine) { 54 | lines.push(`
`); 55 | } 56 | lines.push( 57 | `${row.name}: ${row.description}.`, 58 | ); 59 | if (hideLine) { 60 | lines.push(hideLine); 61 | lines.push('
'); 62 | } 63 | }); 64 | return lines; 65 | } 66 | 67 | /** 68 | * @deprecated 69 | */ 70 | protected printAsList(rows: Row[]) { 71 | const lines: string[] = []; 72 | rows.forEach((row) => { 73 | let line = `- \`${row.name}\``; 74 | const descriptions: string[] = []; 75 | if (row.description) { 76 | descriptions.push(row.description); 77 | } 78 | if (row.type) { 79 | descriptions.push(`type: \`${this.printJson(row.type)}\``); 80 | } 81 | if (row.default !== undefined) { 82 | descriptions.push(`default: \`${this.printJson(row.default)}\``); 83 | } 84 | if (descriptions.length) { 85 | line += ': ' + descriptions.join(', '); 86 | } 87 | lines.push(line); 88 | }); 89 | return lines; 90 | } 91 | 92 | async attach(headLevel: number, attachTitle: string, markdownPath: string) { 93 | const markdown = await fsp.readFile(markdownPath, 'utf8'); 94 | const markdownLines = markdown.split('\n'); 95 | let startIndex = markdownLines.findIndex((line) => 96 | new RegExp('#'.repeat(headLevel) + '\\s*' + attachTitle + '\\s*').test( 97 | line, 98 | ), 99 | ); 100 | if (startIndex < 0) { 101 | return; 102 | } 103 | startIndex += 1; 104 | const endIndex = markdownLines 105 | .slice(startIndex) 106 | .findIndex((line) => new RegExp(`#{1,${headLevel}}[^#]`).test(line)); 107 | const removeCount = endIndex < 0 ? 0 : endIndex; 108 | 109 | const sections = await this.generate(); 110 | const lines: string[] = ['', this.hint, this.ignorePrettierStart]; 111 | for (const section of sections) { 112 | if (section.title) { 113 | lines.push(`${section.title}`); 114 | } 115 | lines.push(...this.printAsDetails(section.rows)); 116 | } 117 | lines.push(''); 118 | lines.push(this.ignorePrettierEnd); 119 | lines.push(''); 120 | markdownLines.splice(startIndex, removeCount, ...lines); 121 | console.log(markdownLines.join('\n')) 122 | await fsp.writeFile(markdownPath, markdownLines.join('\n')); 123 | console.log(`Attached to ${attachTitle} header`); 124 | } 125 | } 126 | 127 | class ConfigurationDocGenerator extends DocGenerator { 128 | constructor( 129 | generateCommand: string, 130 | public packageDeclarationFilepath: string, 131 | ) { 132 | super(generateCommand); 133 | } 134 | 135 | isNodeExported(node: ts.Node) { 136 | return ( 137 | (ts.getCombinedModifierFlags(node as ts.Declaration) & 138 | ts.ModifierFlags.Export) !== 139 | 0 || 140 | (!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile) 141 | ); 142 | } 143 | 144 | async generate() { 145 | const defRows: Row[] = []; 146 | const propRows: Row[] = []; 147 | 148 | const conf = Pkg.contributes.configuration; 149 | const title = conf.title; 150 | const filename = pathLib.basename(this.packageDeclarationFilepath); 151 | 152 | const Kind = ts.SyntaxKind; 153 | const prog = ts.createProgram([this.packageDeclarationFilepath], { 154 | strict: true, 155 | }); 156 | const sourceFile = prog.getSourceFile(this.packageDeclarationFilepath)!; 157 | const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); 158 | const checker = prog.getTypeChecker(); 159 | 160 | function print(node: ts.Node): string { 161 | return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); 162 | } 163 | 164 | function debug(node: ts.Node) { 165 | console.log(Kind[node.kind]); 166 | console.log(print(node)); 167 | } 168 | 169 | sourceFile.forEachChild((node) => { 170 | if (!this.isNodeExported(node)) { 171 | return; 172 | } 173 | 174 | if (ts.isTypeAliasDeclaration(node)) { 175 | defRows.push({ 176 | name: node.name.text, 177 | description: node.name.text, 178 | type: print(node.type), 179 | }); 180 | } else if (ts.isInterfaceDeclaration(node)) { 181 | if (node.name.text === title) { 182 | node.forEachChild((prop) => { 183 | if (!ts.isPropertySignature(prop)) { 184 | return; 185 | } 186 | const symbol = checker.getSymbolAtLocation(prop.name); 187 | if (!symbol) { 188 | return; 189 | } 190 | 191 | const name = symbol.getName(); 192 | // @ts-ignore 193 | const jsonProp = conf.properties[name as any] as Definition & { 194 | default_doc?: string; 195 | }; 196 | propRows.push({ 197 | name, 198 | description: ts.displayPartsToString( 199 | symbol.getDocumentationComment(checker), 200 | ), 201 | type: prop.type ? print(prop.type) : undefined, 202 | default: jsonProp.default_doc 203 | ? jsonProp.default_doc 204 | : jsonProp.default, 205 | }); 206 | }); 207 | } 208 | } else { 209 | console.error(`[gen_doc] ${filename} not support ${print(node)}`); 210 | } 211 | }); 212 | 213 | return [ 214 | { title: 'Properties', rows: propRows }, 215 | ]; 216 | } 217 | } 218 | 219 | // class CommandDocGenerator extends DocGenerator { 220 | // async generate() { 221 | // const cmds = Pkg.contributes.commands as Cmd[]; 222 | // const rows: Row[] = []; 223 | // cmds.forEach((cmd) => { 224 | // rows.push({ 225 | // name: cmd.command, 226 | // description: cmd.title, 227 | // }); 228 | // }); 229 | // return [{ rows }]; 230 | // } 231 | // } 232 | 233 | async function main() { 234 | const cmd = 'yarn run bulid:doc'; 235 | const markdownPath = `${__dirname}/../README.md` 236 | const packageDeclarationFilepath = `${__dirname}/../src/types/pkg-config.d.ts` 237 | // await new CommandDocGenerator(cmd).attach(2, 'Commands', markdownPath); 238 | await new ConfigurationDocGenerator(cmd, packageDeclarationFilepath).attach( 239 | 2, 240 | 'Configuration', 241 | markdownPath 242 | ); 243 | } 244 | 245 | main().catch(console.error); 246 | -------------------------------------------------------------------------------- /scripts/readme.md: -------------------------------------------------------------------------------- 1 | Reference: https://github.com/weirongxu/coc-explorer/tree/master/scripts 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Position, 3 | ProviderResult, 4 | TextDocument, 5 | ExtensionContext, 6 | CompletionItem, 7 | CompletionItemKind, 8 | CompletionItemProvider, 9 | languages, 10 | workspace, 11 | } from 'coc.nvim' 12 | 13 | export async function activate(context: ExtensionContext): Promise { 14 | const { subscriptions } = context 15 | const config = workspace.getConfiguration('just-complete') 16 | 17 | subscriptions.push( 18 | languages.registerCompletionItemProvider( 19 | 'coc-just-complete', 20 | config.get('shortcut'), 21 | null, 22 | new JustCompleteProvider(), 23 | ['_'], 24 | config.get('priority'), 25 | [], 26 | ) 27 | ) 28 | } 29 | 30 | export class JustCompleteProvider implements CompletionItemProvider { 31 | constructor() {} 32 | 33 | provideCompletionItems(document: TextDocument, position: Position): ProviderResult { 34 | const doc = workspace.getDocument(document.uri) 35 | if (!doc) return [] 36 | 37 | const wordRange = doc.getWordRangeAtPosition(Position.create(position.line, position.character - 1)) 38 | if (!wordRange) return [] 39 | 40 | const preWord = document.getText(wordRange) 41 | if (preWord.indexOf('_') < 0) return [] 42 | 43 | const prePreWord = preWord.slice(0, preWord.lastIndexOf('_')) 44 | 45 | return this.gatherWords() 46 | .filter(word => 47 | word.indexOf(prePreWord) < 0 && 48 | prePreWord.indexOf(word) < 0) 49 | .map(word => ({ 50 | "label": `${prePreWord}_${word}`, 51 | "kind": CompletionItemKind.Text, 52 | "insertText": `${prePreWord}_${word}` 53 | })) 54 | } 55 | 56 | private gatherWords(): string[] { 57 | const words: string[] = [] 58 | workspace.documents.forEach(document => { 59 | if (document['isIgnored']) return 60 | for (const word of document['words'] as string[]) { 61 | for (const word_no_underscore of word.split('_')) { 62 | if (!words.includes(word_no_underscore) && word_no_underscore.length >= 3) { 63 | words.push(word_no_underscore) 64 | } 65 | } 66 | } 67 | }) 68 | return words 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/types/pkg-config.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /** 3 | * This file was automatically generated by json-schema-to-typescript. 4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 5 | * and run json-schema-to-typescript to regenerate this file. 6 | */ 7 | 8 | export interface JustComplete { 9 | 'just-complete.shortcut'?: string; 10 | 'just-complete.priority'?: number; 11 | [k: string]: unknown; 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@voldikss/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "outDir": "lib", 6 | "target": "es2015", 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "lib": ["es2018"], 10 | "plugins": [] 11 | }, 12 | "include": ["src"], 13 | "exclude": [] 14 | } 15 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.ts', 5 | target: 'node', 6 | mode: 'none', 7 | resolve: { 8 | mainFields: ['module', 'main'], 9 | extensions: ['.js', '.ts'] 10 | }, 11 | externals: { 12 | 'coc.nvim': 'commonjs coc.nvim' 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.ts$/, 18 | exclude: /node_modules/, 19 | use: [ 20 | { 21 | loader: 'ts-loader', 22 | options: { 23 | compilerOptions: { 24 | sourceMap: true 25 | } 26 | } 27 | } 28 | ] 29 | } 30 | ] 31 | }, 32 | output: { 33 | path: path.join(__dirname, 'lib'), 34 | filename: 'index.js', 35 | libraryTarget: 'commonjs' 36 | }, 37 | plugins: [], 38 | node: { 39 | __dirname: false, 40 | __filename: false 41 | } 42 | }; 43 | --------------------------------------------------------------------------------