mirror of
https://github.com/amix/vimrc
synced 2025-09-15 16:45:00 +08:00
Add support with Go language.
This commit is contained in:
@ -0,0 +1,63 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:Text = 1
|
||||
let s:Method = 2
|
||||
let s:Function = 3
|
||||
let s:Constructor = 4
|
||||
let s:Field = 5
|
||||
let s:Variable = 6
|
||||
let s:Class = 7
|
||||
let s:Interface = 8
|
||||
let s:Module = 9
|
||||
let s:Property = 10
|
||||
let s:Unit = 11
|
||||
let s:Value = 12
|
||||
let s:Enum = 13
|
||||
let s:Keyword = 14
|
||||
let s:Snippet = 15
|
||||
let s:Color = 16
|
||||
let s:File = 17
|
||||
let s:Reference = 18
|
||||
let s:Folder = 19
|
||||
let s:EnumMember = 20
|
||||
let s:Constant = 21
|
||||
let s:Struct = 22
|
||||
let s:Event = 23
|
||||
let s:Operator = 24
|
||||
let s:TypeParameter = 25
|
||||
|
||||
function! go#lsp#completionitemkind#Vim(kind) abort
|
||||
if a:kind == s:Method || a:kind == s:Function || a:kind == s:Constructor
|
||||
return 'f'
|
||||
elseif a:kind == s:Variable || a:kind == s:Constant
|
||||
return 'v'
|
||||
elseif a:kind == s:Field || a:kind == s:Property
|
||||
return 'm'
|
||||
elseif a:kind == s:Class || a:kind == s:Interface || a:kind == s:Struct
|
||||
return 't'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! go#lsp#completionitemkind#IsFunction(kind) abort
|
||||
if a:kind == s:Function
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! go#lsp#completionitemkind#IsMethod(kind) abort
|
||||
if a:kind == s:Method
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
19
sources_non_forked/vim-go/autoload/go/lsp/filechangetype.vim
Normal file
19
sources_non_forked/vim-go/autoload/go/lsp/filechangetype.vim
Normal file
@ -0,0 +1,19 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:fct = {
|
||||
\ 'Created': 1,
|
||||
\ 'Changed': 2,
|
||||
\ 'Deleted': 3,
|
||||
\ }
|
||||
|
||||
function! go#lsp#filechangetype#FileChangeType(name)
|
||||
return s:fct[a:name]
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
72
sources_non_forked/vim-go/autoload/go/lsp/lsp.vim
Normal file
72
sources_non_forked/vim-go/autoload/go/lsp/lsp.vim
Normal file
@ -0,0 +1,72 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
" go#lsp#lsp#Position returns the LSP text position. If no arguments are
|
||||
" provided, the cursor position is assumed. Otherwise, there should be two
|
||||
" arguments: the line and the column.
|
||||
function! go#lsp#lsp#Position(...)
|
||||
if a:0 < 2
|
||||
let [l:line, l:col] = getpos('.')[1:2]
|
||||
else
|
||||
let l:line = a:1
|
||||
let l:col = a:2
|
||||
endif
|
||||
let l:content = getline(l:line)
|
||||
|
||||
" LSP uses 0-based lines.
|
||||
return [l:line - 1, s:character(l:line, l:col-1)]
|
||||
endfunction
|
||||
|
||||
function! s:strlen(str) abort
|
||||
let l:runes = split(a:str, '\zs')
|
||||
return len(l:runes) + len(filter(l:runes, 'char2nr(v:val)>=0x10000'))
|
||||
endfunction
|
||||
|
||||
function! s:character(line, col) abort
|
||||
return s:strlen(getline(a:line)[:col([a:line, a:col - 1])])
|
||||
endfunction
|
||||
|
||||
" go#lsp#PositionOf returns len(content[0:units]) where units is utf-16 code
|
||||
" units. This is mostly useful for converting LSP text position to vim
|
||||
" position.
|
||||
function! go#lsp#lsp#PositionOf(content, units, ...) abort
|
||||
if a:units == 0
|
||||
return 1
|
||||
endif
|
||||
|
||||
let l:remaining = a:units
|
||||
let l:str = ''
|
||||
for l:rune in split(a:content, '\zs')
|
||||
if l:remaining < 0
|
||||
break
|
||||
endif
|
||||
let l:remaining -= 1
|
||||
if char2nr(l:rune) >= 0x10000
|
||||
let l:remaining -= 1
|
||||
endif
|
||||
let l:str = l:str . l:rune
|
||||
endfor
|
||||
|
||||
return len(l:str)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#lsp#SeverityToErrorType(severity) abort
|
||||
if a:severity == 1
|
||||
return 'E'
|
||||
elseif a:severity == 2
|
||||
return 'W'
|
||||
elseif a:severity == 3
|
||||
return 'I'
|
||||
elseif a:severity == 4
|
||||
return 'I'
|
||||
endif
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
32
sources_non_forked/vim-go/autoload/go/lsp/lsp_test.vim
Normal file
32
sources_non_forked/vim-go/autoload/go/lsp/lsp_test.vim
Normal file
@ -0,0 +1,32 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
function! Test_PositionOf_Simple()
|
||||
let l:actual = go#lsp#lsp#PositionOf("just ascii", 3)
|
||||
call assert_equal(4, l:actual)
|
||||
endfunc
|
||||
|
||||
|
||||
function! Test_PositionOf_MultiByte()
|
||||
" ⌘ is U+2318, which encodes to three bytes in utf-8 and 1 code unit in
|
||||
" utf-16.
|
||||
let l:actual = go#lsp#lsp#PositionOf("⌘⌘ foo", 3)
|
||||
call assert_equal(8, l:actual)
|
||||
endfunc
|
||||
|
||||
function! Test_PositionOf_MultipleCodeUnit()
|
||||
" 𐐀 is U+10400, which encodes to 4 bytes in utf-8 and 2 code units in
|
||||
" utf-16.
|
||||
let l:actual = go#lsp#lsp#PositionOf("𐐀 bar", 3)
|
||||
call assert_equal(6, l:actual)
|
||||
endfunction
|
||||
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
438
sources_non_forked/vim-go/autoload/go/lsp/message.vim
Normal file
438
sources_non_forked/vim-go/autoload/go/lsp/message.vim
Normal file
@ -0,0 +1,438 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! go#lsp#message#Initialize(wd) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'initialize',
|
||||
\ 'params': {
|
||||
\ 'processId': getpid(),
|
||||
\ 'rootUri': go#path#ToURI(a:wd),
|
||||
\ 'capabilities': {
|
||||
\ 'workspace': {
|
||||
\ 'workspaceFolders': v:true,
|
||||
\ 'didChangeConfiguration': {
|
||||
\ 'dynamicRegistration': v:true,
|
||||
\ },
|
||||
\ 'workspaceEdit': {
|
||||
\ 'documentChanges': v:true,
|
||||
\ },
|
||||
\ 'configuration': v:true,
|
||||
\ },
|
||||
\ 'textDocument': {
|
||||
\ 'hover': {
|
||||
\ 'contentFormat': ['plaintext'],
|
||||
\ },
|
||||
\ 'completion': {
|
||||
\ 'completionItem': {
|
||||
\ 'snippetSupport': go#config#GoplsUsePlaceholders() ? v:true : v:false,
|
||||
\ },
|
||||
\ },
|
||||
\ 'codeAction': {
|
||||
\ 'codeActionLiteralSupport': {
|
||||
\ 'codeActionKind': {
|
||||
\ 'valueSet': ['source.organizeImports', 'refactor.rewrite'],
|
||||
\ },
|
||||
\ },
|
||||
\ },
|
||||
\ }
|
||||
\ },
|
||||
\ 'workspaceFolders': [s:workspaceFolder(0, a:wd)],
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Initialized() abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'initialized',
|
||||
\ 'params': {},
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Shutdown() abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'shutdown',
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Format(file) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/formatting',
|
||||
\ 'params': {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': go#path#ToURI(a:file)
|
||||
\ },
|
||||
\ 'options': {
|
||||
\ 'insertSpaces': v:false,
|
||||
\ },
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#CodeActionImports(file) abort
|
||||
return s:codeAction('source.organizeImports', a:file)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#CodeActionFillStruct(file, line, col) abort
|
||||
return go#lsp#message#CodeActionRefactorRewrite(a:file, a:line, a:col, a:line, a:col)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#CodeActionRefactorRewrite(file, startline, startcol, endline, endcol) abort
|
||||
let l:startpos = s:position(a:startline, a:startcol)
|
||||
let l:endpos = s:position(a:endline, a:endcol)
|
||||
|
||||
let l:request = s:codeAction('refactor.rewrite', a:file)
|
||||
|
||||
let l:request.params = extend(l:request.params,
|
||||
\ {
|
||||
\ 'range': {
|
||||
\ 'start': l:startpos,
|
||||
\ 'end': l:endpos,
|
||||
\ }
|
||||
\ })
|
||||
|
||||
return l:request
|
||||
endfunction
|
||||
|
||||
function! s:codeAction(name, file) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/codeAction',
|
||||
\ 'params': {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': go#path#ToURI(a:file)
|
||||
\ },
|
||||
\ 'range': {
|
||||
\ 'start': s:position(0, 0),
|
||||
\ 'end': s:position(line('$'), 0),
|
||||
\ },
|
||||
\ 'context': {
|
||||
\ 'only': [a:name],
|
||||
\ },
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Exit() abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'exit',
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#WorkspaceFoldersResult(dirs) abort
|
||||
return map(copy(a:dirs), function('s:workspaceFolder', []))
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Definition(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/definition',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#TypeDefinition(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/typeDefinition',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Implementation(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/implementation',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#DidOpen(file, content, version) abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'textDocument/didOpen',
|
||||
\ 'params': {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': go#path#ToURI(a:file),
|
||||
\ 'languageId': 'go',
|
||||
\ 'text': a:content,
|
||||
\ 'version': a:version,
|
||||
\ }
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#DidChange(file, content, version) abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'textDocument/didChange',
|
||||
\ 'params': {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': go#path#ToURI(a:file),
|
||||
\ 'version': a:version,
|
||||
\ },
|
||||
\ 'contentChanges': [
|
||||
\ {
|
||||
\ 'text': a:content,
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#DidChangeWatchedFile(file, ct) abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'workspace/didChangeWatchedFiles',
|
||||
\ 'params': {
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'uri': go#path#ToURI(a:file),
|
||||
\ 'type': go#lsp#filechangetype#FileChangeType(a:ct),
|
||||
\ },
|
||||
\ ],
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#DidClose(file) abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'textDocument/didClose',
|
||||
\ 'params': {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': go#path#ToURI(a:file),
|
||||
\ }
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Completion(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/completion',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#References(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
let l:params.context = {'includeDeclaration': v:true}
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/references',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#PrepareCallHierarchy(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/prepareCallHierarchy',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#IncomingCalls(item) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'callHierarchy/incomingCalls',
|
||||
\ 'params': {
|
||||
\ 'item': a:item,
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Hover(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/hover',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Rename(file, line, col, newName) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
let l:params.newName = a:newName
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/rename',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#ChangeWorkspaceFolders(add, remove) abort
|
||||
let l:addDirs = map(copy(a:add), function('s:workspaceFolder', []))
|
||||
let l:removeDirs = map(copy(a:remove), function('s:workspaceFolder', []))
|
||||
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'workspace/didChangeWorkspaceFolders',
|
||||
\ 'params': {
|
||||
\ 'event': {
|
||||
\ 'removed': l:removeDirs,
|
||||
\ 'added': l:addDirs,
|
||||
\ },
|
||||
\ }
|
||||
\ }
|
||||
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#ConfigurationResult(items) abort
|
||||
let l:result = []
|
||||
|
||||
" results must be in the same order as the items
|
||||
for l:item in a:items
|
||||
let l:workspace = go#path#FromURI(l:item.scopeUri)
|
||||
let l:config = {
|
||||
\ 'buildFlags': [],
|
||||
\ 'hoverKind': 'Structured',
|
||||
\ }
|
||||
let l:buildtags = go#config#BuildTags()
|
||||
if buildtags isnot ''
|
||||
let l:config.buildFlags = extend(l:config.buildFlags, ['-tags', go#config#BuildTags()])
|
||||
endif
|
||||
|
||||
let l:deepCompletion = go#config#GoplsDeepCompletion()
|
||||
let l:matcher = go#config#GoplsMatcher()
|
||||
let l:completeUnimported = go#config#GoplsCompleteUnimported()
|
||||
let l:staticcheck = go#config#GoplsStaticCheck()
|
||||
let l:usePlaceholder = go#config#GoplsUsePlaceholders()
|
||||
let l:tempModfile = go#config#GoplsTempModfile()
|
||||
let l:analyses = go#config#GoplsAnalyses()
|
||||
let l:local = go#config#GoplsLocal()
|
||||
if type(l:local) is v:t_dict
|
||||
let l:local = get(l:local, l:workspace, v:null)
|
||||
endif
|
||||
let l:gofumpt = go#config#GoplsGofumpt()
|
||||
let l:settings = go#config#GoplsSettings()
|
||||
|
||||
if l:deepCompletion isnot v:null
|
||||
if l:deepCompletion
|
||||
let l:config.deepCompletion = v:true
|
||||
else
|
||||
let l:config.deepCompletion = v:false
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:matcher isnot v:null
|
||||
let l:config.matcher = l:matcher
|
||||
endif
|
||||
|
||||
if l:completeUnimported isnot v:null
|
||||
if l:completeUnimported
|
||||
let l:config.completeUnimported = v:true
|
||||
else
|
||||
let l:config.completeUnimported = v:false
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:staticcheck isnot v:null
|
||||
if l:staticcheck
|
||||
let l:config.staticcheck = v:true
|
||||
else
|
||||
let l:config.staticcheck = v:false
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:usePlaceholder isnot v:null
|
||||
if l:usePlaceholder
|
||||
let l:config.usePlaceholders = v:true
|
||||
else
|
||||
let l:config.usePlaceholders = v:false
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:tempModfile isnot v:null
|
||||
if l:tempModfile
|
||||
let l:config.tempModfile = v:true
|
||||
else
|
||||
let l:config.tempModfile = v:false
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:analyses isnot v:null
|
||||
let l:config.analyses = l:analyses
|
||||
endif
|
||||
|
||||
if l:local isnot v:null
|
||||
let l:config.local = l:local
|
||||
endif
|
||||
|
||||
if l:gofumpt isnot v:null
|
||||
if l:gofumpt
|
||||
let l:config.gofumpt = v:true
|
||||
else
|
||||
let l:config.gofumpt = v:false
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:settings isnot v:null
|
||||
let l:config = extend(l:config, l:settings, 'keep')
|
||||
endif
|
||||
|
||||
let l:result = add(l:result, deepcopy(l:config))
|
||||
endfor
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#ExecuteCommand(cmd, args) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'workspace/executeCommand',
|
||||
\ 'params': {
|
||||
\ 'command': a:cmd,
|
||||
\ 'arguments': a:args,
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#ApplyWorkspaceEditResponse(ok) abort
|
||||
return {
|
||||
\ 'applied': a:ok,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#PrepareRename(file, line, col) abort
|
||||
let l:params = s:textDocumentPositionParams(a:file, a:line, a:col)
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'textDocument/prepareRename',
|
||||
\ 'params': l:params,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! s:workspaceFolder(key, val) abort
|
||||
return {'uri': go#path#ToURI(a:val), 'name': a:val}
|
||||
endfunction
|
||||
|
||||
function! s:position(line, col) abort
|
||||
return {'line': a:line, 'character': a:col}
|
||||
endfunction
|
||||
|
||||
function! s:textDocumentPositionParams(fname, line, col) abort
|
||||
return {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': go#path#ToURI(a:fname)
|
||||
\ },
|
||||
\ 'position': s:position(a:line, a:col),
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
Reference in New Issue
Block a user