The execution result started on Vim is basically output to another buffer divided into windows, but
--The screen for source code becomes narrower --I want to output the result to the sub monitor
There was a personal dissatisfaction.
So, I thought that I wish I could get the result in the floating window like IDE, so I decided to implement it. The demo screen at the beginning shows what it looks like. Rust is executed with QuickRun, and the execution result that is originally output to the buffer is sent to another Vim via the Python server and output.
If you can communicate directly with other Vim processes from Vim, for example, if you can use the main editor side as a server, you can omit the Python server, but since I have not investigated it in detail, I modified the Python server enclosed as a sample in Vim and socketed it. I took the method of passing the execution result by communication.
These implementation sets are summarized as a plug-in, QuickRun is an example of actual use, and this plug-in provides a set of processing to send and receive the character string you want to pass by socket communication.
I will explain how to implement the demo at the beginning.
Since QuickRun will output the execution result to the outputter as it is, use the hook function to save the contents to the cache before output, and send the result to the Python server when the execution is completed.
At this time, set outputter/buffer/close_on_empty
to 1 so that the split window for output does not open on the main editor side.
" .vimrc
let g:quickrun_config = {
\ 'rust-run-modeless': {
\ 'command' : 'cargo',
\ 'cmdopt': 'run',
\ 'args': '--quiet',
\ 'exec' : '%c %o %a',
\ 'outputter': 'buffer',
\ 'outputter/buffer/close_on_empty': 1,
\ },
\ }
let s:hook = {
\ 'name': 'modeless',
\ 'kind': 'hook',
\ 'output_data': '',
\ }
function! s:hook.on_output(data, session)
let self.output_data .= a:session.data
let a:session.data = '' " empty output
endfunction
function! s:hook.on_exit(session, context)
"Send execution result
let l:txt = split(self.output_data, '\r\n\|\n')
call modeless_window#send(l:txt)
echohl MoreMsg
echomsg 'output: to modeless'
echohl NONE
endfunction
call quickrun#module#register(s:hook, 1)
unlet s:hook
Socket communication uses Vim's channel, ch_open
opens two for sending and receiving with a fixed port number, but the main editor opens only for sending and sends with ch_sendexpr
.
function! modeless_window#send(txt)
let l:ch = ch_open('127.0.0.1:'.s:send_port)
let l:st = ch_status(l:ch)
if l:st ==# 'fail' || l:st ==# 'closed'
else
call ch_sendexpr(l:ch, a:txt)
endif
endfunction
On the other hand, Vim started as a modeless window on the receiving side, so it also uses ch_sendexpr
to execute asynchronously and waits for a reply from the server.
function! modeless_window#run() abort
"Called from the main editor side and started
call vimproc#system_bg(
\ 'gvim' .
\ ' +"set columns=80"' .
\ ' +"set lines=20"' .
\ ' +"set titlestring=[MODELESS_WINDOW]"' .
\ ' +"set bufhidden=hide buftype=nofile noswapfile nobuflisted"' .
\ ' +"call modeless_window#_recv()"')
endfunction
let s:ch = v:null
function! modeless_window#_recv()
if s:ch == v:null
let s:ch = ch_open('127.0.0.1:'.s:recv_port)
endif
let l:st = ch_status(s:ch)
if l:st ==# 'fail' || l:st ==# 'closed'
:q!
return
endif
call ch_sendexpr(s:ch, 'message', {'callback': 'modeless_window#_recv_handler'})
endfunction
function! modeless_window#_recv_handler(handle, msg)
if a:handle ==# 'fail' || a:handle ==# 'closed'
:q!
return
endif
"Helper function that replaces buffer contents with string arguments
call modeless_window#utils#settext(a:msg)
"next
call modeless_window#_recv()
endfunction
Even though it is a server, it is simple because it just opens two sockets and passes the transmitted contents as it is. Vim comes with a socket communication sample (tools/demoserver.py
) made by Python. I am remodeling.
#!/usr/bin/python
# vim-modeless-window/tools/modeless_server.py
#
# from) $VIM/tools/demoserver.Borrow py
#
# This requires Python 2.6 or later.
from __future__ import print_function
import json
import socket
import sys
import threading
import time
try:
# Python 3
import socketserver
except ImportError:
# Python 2
import SocketServer as socketserver
shared_message = None
lock = None
#For sending messages
class ThreadedTCPSendHandler(socketserver.BaseRequestHandler):
def handle(self):
print("=== [S] socket opened ===")
global shared_message
global lock
while True:
try:
data = self.request.recv(40960).decode('utf-8') # NOTE:Appropriate size
except socket.error:
print("=== [S] socket error ===")
break
except IOError:
print("=== [S] socket closed ===")
break
if data == '':
print("=== [S] socket closed ===")
break
# print("received: {0}".format(data))
try:
decoded = json.loads(data)
except ValueError:
print("json decoding failed")
decoded = [-1, '']
#Acquisition of books
response = ''
if decoded[0] >= 0:
with lock:
shared_message = decoded[1]
response = 'sended!'
else:
response = 'what?'
encoded = json.dumps([decoded[0], response])
print("sending {0}".format(encoded))
self.request.sendall(encoded.encode('utf-8'))
#For receiving messages
class ThreadedTCPReceiveHandler(socketserver.BaseRequestHandler):
def handle(self):
print("=== [R] socket opened ===")
global shared_message
global lock
while True:
try:
data = self.request.recv(4096).decode('utf-8')
except socket.error:
print("=== [R] socket error ===")
break
except IOError:
print("=== [R] socket closed ===")
break
if data == '':
print("=== [R] socket closed ===")
break
print("received: {0}".format(data))
try:
decoded = json.loads(data)
except ValueError:
print("json decoding failed")
decoded = [-1, '']
if decoded[0] >= 0:
if decoded[1] == 'message':
#Wait for the message
# FIXME:It won't end until it's received
while True:
message = None
with lock:
if shared_message != None:
message = shared_message
shared_message = None
if message != None:
encoded = json.dumps([decoded[0], message])
print('sending message text')
self.request.sendall(encoded.encode('utf-8'))
break
time.sleep(0.1)
else:
#Request you don't know
print('unknown request {0}'.format(decoded[1]))
pass
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def send_server():
HOST, PORT = "localhost", 13578
return server(HOST, PORT, ThreadedTCPSendHandler)
def recv_server():
HOST, PORT = "localhost", 13579
return server(HOST, PORT, ThreadedTCPReceiveHandler)
def server(host, port, tcpHandler):
server = ThreadedTCPServer((host, port), tcpHandler)
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread: ", server_thread.name)
print("Listening on port {0}".format(port))
return server
if __name__ == "__main__":
lock = threading.RLock()
send_server = send_server()
recv_server = recv_server()
while True:
typed = sys.stdin.readline()
if "quit" in typed:
print("Goodbye!")
break
send_server.shutdown()
send_server.server_close()
recv_server.shutdown()
recv_server.server_close()
Recommended Posts