import { Position , TextDocument , TextEdit } from 'vscode-languageserver-textdocument' ; import { window , workspace } from 'coc.nvim' ; import { URI } from 'vscode-uri' ; import { GoTagsConfig } from './config' ; import { execTool } from './tools' ; import { GOMODIFYTAGS } from '../binaries' ; interface Params { prompt ? : boolean ; tags ? : string [ ] ; selection ? : "line" | "struct" ; } interface ClearParams { selection ? : "line" | "struct" ; } export async function addTags ( document , params = { } ) { const config = workspace . getConfiguration ( ) . get ( 'go.tags' , { } ) as GoTagsConfig ; let tags = ( params . tags && params . tags . length > 0 ) ? params . tags . join ( ',' ) : ( config . tags || 'json' ) ; let options = ( config . options || config . options === "" ) ? config . options : 'json=omitempty' ; const transform = ( config . transform || "snakecase" ) ; const skipUnexported = config . skipUnexported ; let cursor ; if ( params . prompt ) { cursor = await window . getCursorPosition ( ) ; tags = await window . requestInput ( 'Enter comma separated tag names' , tags ) ; if ( ! tags ) { return ; } options = await window . requestInput ( 'Enter comma separated options' , options ) ; } const args = [ '-add-tags' , tags . replace ( / +/g , '' ) , '-override' , '-add-options' , ( options || "" ) , '-transform' , transform , ... ( await offsetArgs ( document , ( params . selection || "struct" ) , cursor ) ) ] ; if ( skipUnexported ) { args . push ( '--skip-unexported' ) ; } await runGomodifytags ( document , args ) ; } export async function removeTags ( document , params = { } ) { const config = workspace . getConfiguration ( ) . get ( 'go.tags' , { } ) as GoTagsConfig ; let tags = ( params . tags && params . tags . length > 0 ) ? params . tags . join ( ',' ) : ( config . tags || 'json' ) ; let cursor ; if ( params . prompt ) { cursor = await window . getCursorPosition ( ) ; tags = await window . requestInput ( 'Enter comma separated tag names' , tags ) ; if ( ! tags ) { return ; } } await runGomodifytags ( document , [ '-remove-tags' , ( tags || "json" ) , '-clear-options' , ... ( await offsetArgs ( document , ( params . selection || "struct" ) , cursor ) ) ] ) ; } export async function clearTags ( document , params = { } ) { await runGomodifytags ( document , [ '-clear-tags' , '-clear-options' , ... ( await offsetArgs ( document , ( params . selection || "struct" ) ) ) ] ) ; } async function runGomodifytags ( document , args ) { const fileName = URI . parse ( document . uri ) . fsPath ; args . push ( '-modified' , '-file' , fileName , '-format' , 'json' ) ; const input = fileArchive ( fileName , document . getText ( ) ) ; const edit = await execGomodifytags ( args , input ) ; await workspace . applyEdit ( { changes : { [ document . uri ] : [ edit ] } } ) ; } interface GomodifytagsOutput { start : number ; end : number ; lines : string [ ] ; } async function execGomodifytags ( args , input ) { try { const stdout = await execTool ( GOMODIFYTAGS , args , input ) ; const mods = JSON . parse ( stdout ) as GomodifytagsOutput ; return { range : { start : { line : mods . start - 1 , character : 0 } , end : { line : mods . end , character : 0 } } , newText : mods . lines . join ( "\n" ) + "\n" } ; } catch ( err ) { window . showMessage ( `Cannot modify tags: ${ err }` , 'error' ) ; throw err ; } } function fileArchive ( fileName , fileContents ) { return fileName + '\n' + Buffer . byteLength ( fileContents , 'utf8' ) + '\n' + fileContents ; } function byteOffsetAt ( document , position ) { const offset = document . offsetAt ( position ) ; const text = document . getText ( ) ; return Buffer . byteLength ( text . substr ( 0 , offset ) ) . toString ( ) ; } async function offsetArgs ( document , selection , cursor = null ) { cursor = cursor || await window . getCursorPosition ( ) ; window . showMessage ( `selection = ${ selection }` ) ; switch ( selection ) { case "struct" : return [ '-offset' , byteOffsetAt ( document , cursor ) ] ; case "line" : return [ '-line' , String ( cursor . line + 1 ) ] ; } }