# =========================================================================== # FILE : deoplete-fsharp.py # AUTHOR : callmekohei # License : MIT license # =========================================================================== import atexit import base64 import functools import os import queue import re import subprocess import threading import time try: import ujson as json except ImportError: import json from deoplete.source.base import Base from deoplete.util import getlines, expand from deoplete.util import debug class Source(Base): def __init__(self, vim): super().__init__(vim) self.name = 'fs' self.mark = '[fs]' self.filetypes = ['fsharp'] self.rank = 900 def on_init(self, context): ### input pattern dotHints = [ r"(\(|<|[a-zA-Z]|\"|\[)*(?<=(\)|>|[a-zA-Z0-9]|\"|\]))\." ] oneWordHints = [ r"^[a-zA-Z]$", "\s*[a-zA-Z]$", "typeof\<[a-zA-Z]$", "(\(\))[a-zA-Z]$", "(\<|\>)[a-zA-Z]$", "(\[|\])[a-zA-Z]$" ] attributeHints = [ r"\[<[a-zA-Z]*" ] self.input_pattern = '|'.join( dotHints + oneWordHints + attributeHints ) ### initialize of deopletefs self.standby = False self.filePath = expand( self.vim.eval( "substitute( expand('%:p') , '\#', '\\#' , 'g' )" ) ) fsc_path = expand( re.split('rplugin', __file__)[0] + expand('bin/deopletefs.exe') ) post_data = { "Row" : -9999 # dummy row , "Col" : 1 , "Line" : '' , "FilePath" : self.filePath , "Source" : '\n'.join( getlines( self.vim ) ) , "Init" : 'true' } self.util = Util(fsc_path, 20) self.util.send(json.dumps(post_data)) start = time.time() self.vim.command("echo '*** deopletefs initializing... ***'") if str(self.util.read()) != '': self.standby = True elapsed_time = time.time() - start self.vim.command("echo '*** finish initialize! *** ( time : " + str(round(elapsed_time,6)) + " s )'") else: elapsed_time = time.time() - start self.vim.command("echo '*** Sorry! Please Re-open file! *** ( time : " + str(round(elapsed_time,6)) + " s )'") def get_complete_position(self, context): m = re.search( r'\w*$', context['input'] ) return m.start() if m else -1 def gather_candidates(self, context): try: if self.standby == False: return [ '=== can not initialize deopletefs ===' ] else: post_data = { "Row" : context['position'][1] , "Col" : context['complete_position'] - 1 , "Line" : context['input'] , "FilePath" : self.filePath , "Source" : '\n'.join( getlines( self.vim ) ) , "Init" : 'false' } self.util.send(json.dumps(post_data)) s = (self.util.read())[0] s = base64.b64decode(s) s = s.decode(encoding='utf-8') lst = s.split("\n") return [ { "word": json_data['word'] , "info": '\n'.join( functools.reduce ( lambda a , b : a + b , json_data['info'] ) ) } for json_data in [ json.loads(s) for s in lst ] ] except Exception as e: return [ str(e) ] class Util(): def __init__(self, exe_path, timeOut_s): self.exe_path = exe_path self.timeOut_s = timeOut_s self.event = threading.Event() self.lines = queue.Queue() ### launch deopletefs if os.name == 'nt': command = [ self.exe_path ] elif os.name == 'posix': command = [ 'mono', self.exe_path ] opts = { 'stdin': subprocess.PIPE, 'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE, 'universal_newlines': True } self.process = subprocess.Popen( command , **opts ) atexit.register(lambda: self.process.kill()) ### create worker thread self.worker = threading.Thread(target=self.work, args=(self,)) self.worker.daemon = True self.worker.start() def work(self,_): while True: data = self.process.stdout.readline() self.lines.put(data, True) self.event.set() def send(self,txt): self.event.clear() self.process.stdin.write(txt + '\n') self.process.stdin.flush() def read(self): self.event.wait(self.timeOut_s) list = [] while True: if self.lines.empty(): break else: list.append( self.lines.get_nowait() ) return list