Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support static json file caching #19

Merged
merged 2 commits into from
May 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
./benchmark
./build
./images
./plugin
./*.tags

# json tarballs
*.tar.gz
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ docs/_build/

# PyBuilder
target/


# json tarballs
*.tar.gz
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM golang:1.6.2-alpine
MAINTAINER zchee <k@zchee.io>

RUN set -ex \
&& apk add --no-cache --virtual .build-deps \
make \
git \
tar \
python3 \
\
&& go get -u -v github.com/nsf/gocode

COPY ./ /deoplete-go

RUN cd /deoplete-go \
&& make gen_json \
\
&& tar cf json_1.6.2_linux_amd64.tar.gz ./data/json/1.6.2/linux_amd64

CMD ["cat", "/deoplete-go/json_1.6.2_linux_amd64.tar.gz"]
34 changes: 34 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@ TARGET = ./rplugin/python3/deoplete/ujson.so
CURRENT := $(shell pwd)
RPLUGIN_HOME := $(CURRENT)/rplugin/python3
PYTHON3 := $(shell which python3)
DOCKER := $(shell which docker)
DOKCER_IMAGE := zchee/deoplete-go:linux_amd64
GIT := $(shell which git)
GOCODE := $(shell which gocode)
GOVERSION = $(shell go version | awk '{print $$3}' | sed -e 's/go//')
GOOS := $(shell go env GOOS)
GOARCH := $(shell go env GOARCH)

RPLUGIN_PATH := ./rplugin/python3/deoplete/sources/
MODULE_NAME := deoplete_go.py

PACKAGE ?= unsafe

ifneq ($(PACKAGE),unsafe)
PACKAGE += unsafe
endif


all : $(TARGET)

Expand All @@ -18,6 +30,28 @@ build/:
rplugin/python3/deoplete/ujson.so: build/
cp $(shell find $(CURRENT)/build -name ujson*.so) $(RPLUGIN_HOME)/deoplete/ujson.so

data/stdlib.txt:
go tool api -contexts $(GOOS)-$(GOARCH)-cgo | sed -e s/,//g | awk '{print $$2}' | uniq > ./data/stdlib.txt
@for pkg in $(PACKAGE) ; do \
echo $$pkg >> ./data/stdlib.txt; \
done
mv ./data/stdlib.txt ./data/stdlib-$(GOVERSION)_$(GOOS)_$(GOARCH).txt

gen_json: data/stdlib.txt
$(GOCODE) close
cd ./data && ./gen_json.py $(GOOS) $(GOARCH)

docker_build:
$(DOCKER) build -t $(DOKCER_IMAGE) .

docker_gen_json: docker_build
$(DOCKER) run --rm $(DOKCER_IMAGE) > ./json_1.6.2_linux_amd64.tar.gz
tar xf ./json_1.6.2_linux_amd64.tar.gz
mv ./json_1.6.2_linux_amd64.tar.gz ./data/json_1.6_linux_amd64.tar.gz

docker_gen_stdlib: docker_build
$(DOCKER) run --rm $(DOKCER_IMAGE) cat /deoplete-go/data/stdlib-1.6.2_linux_amd64.txt > ./data/stdlib-1.6.2_linux_amd64.txt

test: lint

lint: flake8
Expand Down
83 changes: 76 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ Plug 'zchee/deoplete-go', { 'do': 'make'}

## Available Settings

| Setting value | Default | Required |
|:--------------------------------------|:-------:|:---------:|
| `g:deoplete#sources#go#align_class` | `0` | No |
| `g:deoplete#sources#go#gocode_binary` | `''` | Recommend |
| `g:deoplete#sources#go#package_dot` | `0` | No |
| `g:deoplete#sources#go#sort_class` | `[]` | Recommend |
| Setting value | Default | Required |
|:---------------------------------------|:------------------------------:|:---------:|
| `g:deoplete#sources#go#align_class` | `0` | No |
| `g:deoplete#sources#go#gocode_binary` | `''` | Recommend |
| `g:deoplete#sources#go#package_dot` | `0` | No |
| `g:deoplete#sources#go#sort_class` | `[]` | Recommend |
| `g:deoplete#sources#go#use_cache` | `0` | Recommend |
| `g:deoplete#sources#go#json_directory` | `$HOME.'/.config/gocode/json'` | Recommend |

### `g:deoplete#sources#go#align_class`
#### Class Aligning
Expand Down Expand Up @@ -171,6 +173,71 @@ let g:deoplete#sources#go#sort_class = ['package', 'func', 'type', 'var', 'const

Try test it with the `os` package :)

### `g:deoplete#sources#go#use_cache` `g:deoplete#sources#go#json_directory`
#### Static json caching

`g:deoplete#sources#go#use_cache`

| **Default** | `0` |
|--------------|---------------|
| **Required** | **Recommend** |
| **Type** | int |
| **Example** | `1` |

`g:deoplete#sources#go#json_directory`

| **Default** | `~/.cache/deoplete/go/$GOOS_$GOARCH` |
|--------------|------------------------|
| **Required** | **Recommend** |
| **Type** | string |
| **Example** | `'/path/to/data_dir'` |

Use static json caching Go stdlib package API.
If matched name of stdlib and input package name, it returns the static json data without the use of gocode.
and, Possible to get package API if have not `import` of current buffer.

Terms:

- Hook the insert of dot `.` (e.g. `fmt.|`)
- You typed package name have not `import` current buffer
- Match the typed package name and json file name

`deoplete-go` will parse `g:deoplete#sources#go#json_directory` directory. You can define of json data directory.
Default is `~/.cache/deoplete/go/$GOOS_$GOARCH`.

Also, See [How to use static json caching](#how-to-use-static-json-caching)

```vim
let g:deoplete#sources#go#use_cache = 1
let g:deoplete#sources#go#json_directory = '/path/to/data_dir'
```

===

### How to use static json caching

| **Current Go version** | `1.6` |
|------------------------|-----------------------|
| `$GOOS` | `darwin`, `linux` |
| `$GOARCH` | `amd64` |

Pre-generate json data is [data/json](./data/json).
If you use it, `cp -r data/json/VERSION/$GOOS_$GOARCH /path/to/data_dir`.
`/path/to/data_dir` is `g:deoplete#sources#go#json_directory`.

And, You can generate your Go environment. such as version is `devel`, GOARCH is `arm`.
If you want to it, run `make gen_json`.
Will generated json file to `./data/json/VERSION/$GOOS_$GOARCH`.

`make gen_json` command also will create `./data/stdlib.txt`. It same as `go tool api` result.
In detail,
```bash
go tool api -contexts $GOOS-$GOARCH-cgo | grep -v 'golang.org/x/net/http2/hpack' | sed -e s/,//g | awk '{print $2}' | uniq > ./data/stdlib.txt
```

This api list used in the base for the generation of json file.


===

## Sample init.vim
Expand All @@ -191,6 +258,8 @@ let g:deoplete#enable_at_startup = 1
" deoplete-go settings
let g:deoplete#sources#go#gocode_binary = $GOPATH.'/bin/gocode'
let g:deoplete#sources#go#sort_class = ['package', 'func', 'type', 'var', 'const']
let g:deoplete#sources#go#use_cache = 1
let g:deoplete#sources#go#json_directory = '/path/to/data_dir'
```

===
Expand All @@ -199,7 +268,7 @@ TODO:
-----
- [ ] Parse included cgo (C, C++ language) headers on current buffer
- `ctags` will be blocking `deoplete.nvim`
- [ ] Support static json caching
- [x] Support static json caching
- See https://github.com/zchee/deoplete-go/pull/19
- [x] Support Go stdlib package `import "***"` name completion
- This feature has been implemented in gocode. Thanks @nhooyr!
Expand Down
85 changes: 85 additions & 0 deletions data/gen_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
import json
import os
import subprocess
import sys
import re

def main():
base_offset = 41

go_version_raw = subprocess.Popen("go version",
shell=True,
stdout=subprocess.PIPE
).stdout.read()

go_version = str(go_version_raw).split(' ')[2].strip('go')
go_os = sys.argv[1]
go_arch = sys.argv[2]

with open('./stdlib-' + go_version + '_' + go_os + '_' + go_arch + '.txt') as stdlib:
packages = stdlib.read().splitlines()

for pkg in packages:
template_file = './template.go'
f = open(template_file)
fs = f.read(-1)

func = None
if re.search(r'/', pkg):
library = str(pkg).split(r'/')[:-1]
func = str(pkg).split(r'/')[-1]
else:
library = pkg
func = pkg
source = str(fs).replace('IMPORT', pkg).replace('FUNC', func).encode()

offset = base_offset + (len(pkg) + len(func))

process = subprocess.Popen([FindBinaryPath('gocode'),
'-f=json',
'autocomplete',
template_file,
str(offset)],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
start_new_session=True)
process.stdin.write(source)
stdout_data, stderr_data = process.communicate()
result = json.loads(stdout_data.decode())

out_dir = os.path.join(
'./json', go_version, go_os + '_' + go_arch)
if not os.path.exists(out_dir):
os.makedirs(out_dir)

pkg_dir = os.path.join(out_dir, ''.join(library))
if not os.path.exists(pkg_dir):
os.makedirs(pkg_dir)
out_path = \
os.path.join(pkg_dir, func + '.json')
out = open(out_path, 'w')
out.write(json.dumps(result, sort_keys=True))
out.close()

print(pkg)


def FindBinaryPath(cmd):
def is_exec(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

fpath, fname = os.path.split(cmd)
if fpath:
if is_exec(cmd):
return cmd
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
binary = os.path.join(path, cmd)
if is_exec(binary):
return binary
return print('gocode binary not found')

main()
1 change: 1 addition & 0 deletions data/json/1.6.2/darwin_amd64/archive/tar.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0, [{"class": "const", "name": "TypeBlock", "type": ""}, {"class": "const", "name": "TypeChar", "type": ""}, {"class": "const", "name": "TypeCont", "type": ""}, {"class": "const", "name": "TypeDir", "type": ""}, {"class": "const", "name": "TypeFifo", "type": ""}, {"class": "const", "name": "TypeGNULongLink", "type": ""}, {"class": "const", "name": "TypeGNULongName", "type": ""}, {"class": "const", "name": "TypeGNUSparse", "type": ""}, {"class": "const", "name": "TypeLink", "type": ""}, {"class": "const", "name": "TypeReg", "type": ""}, {"class": "const", "name": "TypeRegA", "type": ""}, {"class": "const", "name": "TypeSymlink", "type": ""}, {"class": "const", "name": "TypeXGlobalHeader", "type": ""}, {"class": "const", "name": "TypeXHeader", "type": ""}, {"class": "func", "name": "FileInfoHeader", "type": "func(fi os.FileInfo, link string) (*tar.Header, error)"}, {"class": "func", "name": "NewReader", "type": "func(r io.Reader) *tar.Reader"}, {"class": "func", "name": "NewWriter", "type": "func(w io.Writer) *tar.Writer"}, {"class": "type", "name": "Header", "type": "struct"}, {"class": "type", "name": "Reader", "type": "struct"}, {"class": "type", "name": "Writer", "type": "struct"}, {"class": "var", "name": "ErrFieldTooLong", "type": "error"}, {"class": "var", "name": "ErrHeader", "type": "error"}, {"class": "var", "name": "ErrWriteAfterClose", "type": "error"}, {"class": "var", "name": "ErrWriteTooLong", "type": "error"}]]
1 change: 1 addition & 0 deletions data/json/1.6.2/darwin_amd64/archive/zip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0, [{"class": "const", "name": "Deflate", "type": ""}, {"class": "const", "name": "Store", "type": ""}, {"class": "func", "name": "FileInfoHeader", "type": "func(fi os.FileInfo) (*zip.FileHeader, error)"}, {"class": "func", "name": "NewReader", "type": "func(r io.ReaderAt, size int64) (*zip.Reader, error)"}, {"class": "func", "name": "NewWriter", "type": "func(w io.Writer) *zip.Writer"}, {"class": "func", "name": "OpenReader", "type": "func(name string) (*zip.ReadCloser, error)"}, {"class": "func", "name": "RegisterCompressor", "type": "func(method uint16, comp zip.Compressor)"}, {"class": "func", "name": "RegisterDecompressor", "type": "func(method uint16, dcomp zip.Decompressor)"}, {"class": "type", "name": "Compressor", "type": "func(w io.Writer) (io.WriteCloser, error)"}, {"class": "type", "name": "Decompressor", "type": "func(r io.Reader) io.ReadCloser"}, {"class": "type", "name": "File", "type": "struct"}, {"class": "type", "name": "FileHeader", "type": "struct"}, {"class": "type", "name": "ReadCloser", "type": "struct"}, {"class": "type", "name": "Reader", "type": "struct"}, {"class": "type", "name": "Writer", "type": "struct"}, {"class": "var", "name": "ErrAlgorithm", "type": "error"}, {"class": "var", "name": "ErrChecksum", "type": "error"}, {"class": "var", "name": "ErrFormat", "type": "error"}]]
1 change: 1 addition & 0 deletions data/json/1.6.2/darwin_amd64/bufio/bufio.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0, [{"class": "const", "name": "MaxScanTokenSize", "type": ""}, {"class": "func", "name": "NewReadWriter", "type": "func(r *bufio.Reader, w *bufio.Writer) *bufio.ReadWriter"}, {"class": "func", "name": "NewReader", "type": "func(rd io.Reader) *bufio.Reader"}, {"class": "func", "name": "NewReaderSize", "type": "func(rd io.Reader, size int) *bufio.Reader"}, {"class": "func", "name": "NewScanner", "type": "func(r io.Reader) *bufio.Scanner"}, {"class": "func", "name": "NewWriter", "type": "func(w io.Writer) *bufio.Writer"}, {"class": "func", "name": "NewWriterSize", "type": "func(w io.Writer, size int) *bufio.Writer"}, {"class": "func", "name": "ScanBytes", "type": "func(data []byte, atEOF bool) (advance int, token []byte, err error)"}, {"class": "func", "name": "ScanLines", "type": "func(data []byte, atEOF bool) (advance int, token []byte, err error)"}, {"class": "func", "name": "ScanRunes", "type": "func(data []byte, atEOF bool) (advance int, token []byte, err error)"}, {"class": "func", "name": "ScanWords", "type": "func(data []byte, atEOF bool) (advance int, token []byte, err error)"}, {"class": "type", "name": "ReadWriter", "type": "struct"}, {"class": "type", "name": "Reader", "type": "struct"}, {"class": "type", "name": "Scanner", "type": "struct"}, {"class": "type", "name": "SplitFunc", "type": "func(data []byte, atEOF bool) (advance int, token []byte, err error)"}, {"class": "type", "name": "Writer", "type": "struct"}, {"class": "var", "name": "ErrAdvanceTooFar", "type": "error"}, {"class": "var", "name": "ErrBufferFull", "type": "error"}, {"class": "var", "name": "ErrFinalToken", "type": "error"}, {"class": "var", "name": "ErrInvalidUnreadByte", "type": "error"}, {"class": "var", "name": "ErrInvalidUnreadRune", "type": "error"}, {"class": "var", "name": "ErrNegativeAdvance", "type": "error"}, {"class": "var", "name": "ErrNegativeCount", "type": "error"}, {"class": "var", "name": "ErrTooLong", "type": "error"}]]
1 change: 1 addition & 0 deletions data/json/1.6.2/darwin_amd64/bytes/bytes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0, [{"class": "const", "name": "MinRead", "type": ""}, {"class": "func", "name": "Compare", "type": "func(a []byte, b []byte) int"}, {"class": "func", "name": "Contains", "type": "func(b []byte, subslice []byte) bool"}, {"class": "func", "name": "ContainsAny", "type": "func(b []byte, chars string) bool"}, {"class": "func", "name": "ContainsRune", "type": "func(b []byte, r rune) bool"}, {"class": "func", "name": "Count", "type": "func(s []byte, sep []byte) int"}, {"class": "func", "name": "Equal", "type": "func(a []byte, b []byte) bool"}, {"class": "func", "name": "EqualFold", "type": "func(s []byte, t []byte) bool"}, {"class": "func", "name": "Fields", "type": "func(s []byte) [][]byte"}, {"class": "func", "name": "FieldsFunc", "type": "func(s []byte, f func(rune) bool) [][]byte"}, {"class": "func", "name": "HasPrefix", "type": "func(s []byte, prefix []byte) bool"}, {"class": "func", "name": "HasSuffix", "type": "func(s []byte, suffix []byte) bool"}, {"class": "func", "name": "Index", "type": "func(s []byte, sep []byte) int"}, {"class": "func", "name": "IndexAny", "type": "func(s []byte, chars string) int"}, {"class": "func", "name": "IndexByte", "type": "func(s []byte, c byte) int"}, {"class": "func", "name": "IndexFunc", "type": "func(s []byte, f func(r rune) bool) int"}, {"class": "func", "name": "IndexRune", "type": "func(s []byte, r rune) int"}, {"class": "func", "name": "Join", "type": "func(s [][]byte, sep []byte) []byte"}, {"class": "func", "name": "LastIndex", "type": "func(s []byte, sep []byte) int"}, {"class": "func", "name": "LastIndexAny", "type": "func(s []byte, chars string) int"}, {"class": "func", "name": "LastIndexByte", "type": "func(s []byte, c byte) int"}, {"class": "func", "name": "LastIndexFunc", "type": "func(s []byte, f func(r rune) bool) int"}, {"class": "func", "name": "Map", "type": "func(mapping func(r rune) rune, s []byte) []byte"}, {"class": "func", "name": "NewBuffer", "type": "func(buf []byte) *bytes.Buffer"}, {"class": "func", "name": "NewBufferString", "type": "func(s string) *bytes.Buffer"}, {"class": "func", "name": "NewReader", "type": "func(b []byte) *bytes.Reader"}, {"class": "func", "name": "Repeat", "type": "func(b []byte, count int) []byte"}, {"class": "func", "name": "Replace", "type": "func(s []byte, old []byte, new []byte, n int) []byte"}, {"class": "func", "name": "Runes", "type": "func(s []byte) []rune"}, {"class": "func", "name": "Split", "type": "func(s []byte, sep []byte) [][]byte"}, {"class": "func", "name": "SplitAfter", "type": "func(s []byte, sep []byte) [][]byte"}, {"class": "func", "name": "SplitAfterN", "type": "func(s []byte, sep []byte, n int) [][]byte"}, {"class": "func", "name": "SplitN", "type": "func(s []byte, sep []byte, n int) [][]byte"}, {"class": "func", "name": "Title", "type": "func(s []byte) []byte"}, {"class": "func", "name": "ToLower", "type": "func(s []byte) []byte"}, {"class": "func", "name": "ToLowerSpecial", "type": "func(_case unicode.SpecialCase, s []byte) []byte"}, {"class": "func", "name": "ToTitle", "type": "func(s []byte) []byte"}, {"class": "func", "name": "ToTitleSpecial", "type": "func(_case unicode.SpecialCase, s []byte) []byte"}, {"class": "func", "name": "ToUpper", "type": "func(s []byte) []byte"}, {"class": "func", "name": "ToUpperSpecial", "type": "func(_case unicode.SpecialCase, s []byte) []byte"}, {"class": "func", "name": "Trim", "type": "func(s []byte, cutset string) []byte"}, {"class": "func", "name": "TrimFunc", "type": "func(s []byte, f func(r rune) bool) []byte"}, {"class": "func", "name": "TrimLeft", "type": "func(s []byte, cutset string) []byte"}, {"class": "func", "name": "TrimLeftFunc", "type": "func(s []byte, f func(r rune) bool) []byte"}, {"class": "func", "name": "TrimPrefix", "type": "func(s []byte, prefix []byte) []byte"}, {"class": "func", "name": "TrimRight", "type": "func(s []byte, cutset string) []byte"}, {"class": "func", "name": "TrimRightFunc", "type": "func(s []byte, f func(r rune) bool) []byte"}, {"class": "func", "name": "TrimSpace", "type": "func(s []byte) []byte"}, {"class": "func", "name": "TrimSuffix", "type": "func(s []byte, suffix []byte) []byte"}, {"class": "type", "name": "Buffer", "type": "struct"}, {"class": "type", "name": "Reader", "type": "struct"}, {"class": "var", "name": "ErrTooLarge", "type": "error"}]]
1 change: 1 addition & 0 deletions data/json/1.6.2/darwin_amd64/compress/bzip2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0, [{"class": "func", "name": "NewReader", "type": "func(r io.Reader) io.Reader"}, {"class": "type", "name": "StructuralError", "type": "string"}]]
1 change: 1 addition & 0 deletions data/json/1.6.2/darwin_amd64/compress/flate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[0, [{"class": "const", "name": "BestCompression", "type": ""}, {"class": "const", "name": "BestSpeed", "type": ""}, {"class": "const", "name": "DefaultCompression", "type": ""}, {"class": "const", "name": "HuffmanOnly", "type": ""}, {"class": "const", "name": "NoCompression", "type": ""}, {"class": "func", "name": "NewReader", "type": "func(r io.Reader) io.ReadCloser"}, {"class": "func", "name": "NewReaderDict", "type": "func(r io.Reader, dict []byte) io.ReadCloser"}, {"class": "func", "name": "NewWriter", "type": "func(w io.Writer, level int) (*flate.Writer, error)"}, {"class": "func", "name": "NewWriterDict", "type": "func(w io.Writer, level int, dict []byte) (*flate.Writer, error)"}, {"class": "type", "name": "CorruptInputError", "type": "int64"}, {"class": "type", "name": "InternalError", "type": "string"}, {"class": "type", "name": "ReadError", "type": "struct"}, {"class": "type", "name": "Reader", "type": "interface"}, {"class": "type", "name": "Resetter", "type": "interface"}, {"class": "type", "name": "WriteError", "type": "struct"}, {"class": "type", "name": "Writer", "type": "struct"}]]
Loading