2019-12-30 19:07:04 +00:00
import * as vscode from "vscode" ;
import * as lc from "vscode-languageclient" ;
2020-05-25 12:56:26 +00:00
import * as ra from "./lsp_ext" ;
2021-08-10 13:35:37 +00:00
import * as path from "path" ;
2022-05-17 17:15:06 +00:00
2023-09-05 16:20:08 +00:00
import type { Ctx , Cmd , CtxInit } from "./ctx" ;
2024-02-02 01:38:42 +00:00
import {
applySnippetWorkspaceEdit ,
applySnippetTextEdits ,
type SnippetTextDocumentEdit ,
} from "./snippets" ;
2020-05-25 09:10:31 +00:00
import { spawnSync } from "child_process" ;
2023-07-10 21:10:00 +00:00
import { type RunnableQuickPick , selectRunnable , createTask , createArgs } from "./run" ;
2020-05-25 10:02:30 +00:00
import { AstInspector } from "./ast_inspector" ;
2023-04-05 17:57:01 +00:00
import {
isRustDocument ,
isCargoTomlDocument ,
sleep ,
isRustEditor ,
2023-07-10 21:10:00 +00:00
type RustEditor ,
type RustDocument ,
2023-04-05 17:57:01 +00:00
} from "./util" ;
2020-06-02 12:33:47 +00:00
import { startDebugSession , makeDebugConfig } from "./debug" ;
2023-07-10 21:10:00 +00:00
import type { LanguageClient } from "vscode-languageclient/node" ;
2022-05-16 18:53:00 +00:00
import { LINKED_COMMANDS } from "./client" ;
2023-07-10 21:10:00 +00:00
import type { DependencyId } from "./dependencies_provider" ;
2023-06-27 19:03:53 +00:00
import { unwrapUndefinable } from "./undefinable" ;
2023-10-08 01:52:15 +00:00
import { log } from "./util" ;
2022-05-17 17:15:06 +00:00
2020-05-25 10:02:30 +00:00
export * from "./ast_inspector" ;
export * from "./run" ;
2020-05-25 09:10:31 +00:00
2022-10-28 22:44:37 +00:00
export function analyzerStatus ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
readonly uri = vscode . Uri . parse ( "rust-analyzer-status://status" ) ;
readonly eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
2022-10-17 12:20:14 +00:00
async provideTextDocumentContent ( _uri : vscode.Uri ) : Promise < string > {
2020-05-25 09:10:31 +00:00
if ( ! vscode . window . activeTextEditor ) return "" ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-05-25 09:10:31 +00:00
2020-09-29 20:05:18 +00:00
const params : ra.AnalyzerStatusParams = { } ;
const doc = ctx . activeRustEditor ? . document ;
if ( doc != null ) {
2022-10-17 12:20:14 +00:00
params . textDocument = client . code2ProtocolConverter . asTextDocumentIdentifier ( doc ) ;
2020-09-29 20:05:18 +00:00
}
2022-10-17 12:20:14 +00:00
return await client . sendRequest ( ra . analyzerStatus , params ) ;
2020-05-25 09:10:31 +00:00
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2023-07-11 13:35:10 +00:00
vscode . workspace . registerTextDocumentContentProvider ( "rust-analyzer-status" , tdcp ) ,
2020-05-25 09:10:31 +00:00
) ;
return async ( ) = > {
const document = await vscode . workspace . openTextDocument ( tdcp . uri ) ;
2021-08-09 16:24:43 +00:00
tdcp . eventEmitter . fire ( tdcp . uri ) ;
void ( await vscode . window . showTextDocument ( document , {
viewColumn : vscode.ViewColumn.Two ,
preserveFocus : true ,
} ) ) ;
2020-05-25 09:10:31 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function memoryUsage ( ctx : CtxInit ) : Cmd {
2020-07-07 10:10:14 +00:00
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
readonly uri = vscode . Uri . parse ( "rust-analyzer-memory://memory" ) ;
readonly eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
provideTextDocumentContent ( _uri : vscode.Uri ) : vscode . ProviderResult < string > {
if ( ! vscode . window . activeTextEditor ) return "" ;
2022-10-28 22:44:37 +00:00
return ctx . client . sendRequest ( ra . memoryUsage ) . then ( ( mem : any ) = > {
return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)" ;
} ) ;
2020-07-07 10:10:14 +00:00
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2023-07-11 13:35:10 +00:00
vscode . workspace . registerTextDocumentContentProvider ( "rust-analyzer-memory" , tdcp ) ,
2020-07-07 10:10:14 +00:00
) ;
return async ( ) = > {
tdcp . eventEmitter . fire ( tdcp . uri ) ;
const document = await vscode . workspace . openTextDocument ( tdcp . uri ) ;
return vscode . window . showTextDocument ( document , vscode . ViewColumn . Two , true ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function shuffleCrateGraph ( ctx : CtxInit ) : Cmd {
2021-12-07 14:38:12 +00:00
return async ( ) = > {
2022-10-28 22:44:37 +00:00
return ctx . client . sendRequest ( ra . shuffleCrateGraph ) ;
2021-12-07 14:38:12 +00:00
} ;
}
2023-02-14 13:45:48 +00:00
export function triggerParameterHints ( _ : CtxInit ) : Cmd {
return async ( ) = > {
2023-04-20 19:13:33 +00:00
const parameterHintsEnabled = vscode . workspace
. getConfiguration ( "editor" )
. get < boolean > ( "parameterHints.enabled" ) ;
2023-04-20 16:41:15 +00:00
2023-04-20 19:13:33 +00:00
if ( parameterHintsEnabled ) {
2023-04-20 16:41:15 +00:00
await vscode . commands . executeCommand ( "editor.action.triggerParameterHints" ) ;
}
2023-02-14 13:45:48 +00:00
} ;
}
2023-03-10 09:13:30 +00:00
export function openLogs ( ctx : CtxInit ) : Cmd {
return async ( ) = > {
if ( ctx . client . outputChannel ) {
ctx . client . outputChannel . show ( ) ;
}
} ;
}
2022-10-28 22:44:37 +00:00
export function matchingBrace ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
return async ( ) = > {
const editor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-05-25 09:10:31 +00:00
const response = await client . sendRequest ( ra . matchingBrace , {
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier ( editor . document ) ,
2020-05-25 09:10:31 +00:00
positions : editor.selections.map ( ( s ) = >
2023-07-11 13:35:10 +00:00
client . code2ProtocolConverter . asPosition ( s . active ) ,
2020-05-25 09:10:31 +00:00
) ,
} ) ;
editor . selections = editor . selections . map ( ( sel , idx ) = > {
2023-06-27 19:03:53 +00:00
const position = unwrapUndefinable ( response [ idx ] ) ;
const active = client . protocol2CodeConverter . asPosition ( position ) ;
2020-05-25 09:10:31 +00:00
const anchor = sel . isEmpty ? active : sel.anchor ;
return new vscode . Selection ( anchor , active ) ;
} ) ;
editor . revealRange ( editor . selection ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function joinLines ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
return async ( ) = > {
const editor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-05-25 09:10:31 +00:00
const items : lc.TextEdit [ ] = await client . sendRequest ( ra . joinLines , {
ranges : editor.selections.map ( ( it ) = > client . code2ProtocolConverter . asRange ( it ) ) ,
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier ( editor . document ) ,
2020-05-25 09:10:31 +00:00
} ) ;
2022-05-21 15:38:10 +00:00
const textEdits = await client . protocol2CodeConverter . asTextEdits ( items ) ;
await editor . edit ( ( builder ) = > {
textEdits . forEach ( ( edit : any ) = > {
2020-05-25 09:10:31 +00:00
builder . replace ( edit . range , edit . newText ) ;
} ) ;
2021-02-09 14:12:46 +00:00
} ) ;
2020-05-25 09:10:31 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function moveItemUp ( ctx : CtxInit ) : Cmd {
2023-01-23 14:26:28 +00:00
return moveItem ( ctx , "Up" ) ;
2021-03-16 12:37:00 +00:00
}
2022-10-28 22:44:37 +00:00
export function moveItemDown ( ctx : CtxInit ) : Cmd {
2023-01-23 14:26:28 +00:00
return moveItem ( ctx , "Down" ) ;
2021-03-16 12:37:00 +00:00
}
2022-10-28 22:44:37 +00:00
export function moveItem ( ctx : CtxInit , direction : ra.Direction ) : Cmd {
2021-03-16 12:37:00 +00:00
return async ( ) = > {
const editor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2021-03-16 12:37:00 +00:00
2021-04-13 18:32:45 +00:00
const lcEdits = await client . sendRequest ( ra . moveItem , {
2021-03-16 12:37:00 +00:00
range : client.code2ProtocolConverter.asRange ( editor . selection ) ,
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier ( editor . document ) ,
2021-03-16 12:37:00 +00:00
direction ,
} ) ;
2021-04-13 18:32:45 +00:00
if ( ! lcEdits ) return ;
2021-03-16 14:57:14 +00:00
2022-04-08 11:24:28 +00:00
const edits = await client . protocol2CodeConverter . asTextEdits ( lcEdits ) ;
2021-04-13 18:32:45 +00:00
await applySnippetTextEdits ( editor , edits ) ;
2021-03-16 12:37:00 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function onEnter ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
async function handleKeypress() {
const editor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return false ;
2020-05-25 09:10:31 +00:00
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-05-25 12:12:53 +00:00
const lcEdits = await client
. sendRequest ( ra . onEnter , {
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier (
2023-07-11 13:35:10 +00:00
editor . document ,
2020-09-29 20:05:18 +00:00
) ,
2020-05-25 09:10:31 +00:00
position : client.code2ProtocolConverter.asPosition ( editor . selection . active ) ,
2020-09-01 16:53:07 +00:00
} )
. catch ( ( _error : any ) = > {
// client.handleFailedRequest(OnEnterRequest.type, error, null);
2020-05-25 09:10:31 +00:00
return null ;
} ) ;
2020-05-25 12:12:53 +00:00
if ( ! lcEdits ) return false ;
2020-05-25 09:10:31 +00:00
2022-04-08 11:24:28 +00:00
const edits = await client . protocol2CodeConverter . asTextEdits ( lcEdits ) ;
2020-05-25 12:12:53 +00:00
await applySnippetTextEdits ( editor , edits ) ;
2020-05-25 09:10:31 +00:00
return true ;
}
return async ( ) = > {
if ( await handleKeypress ( ) ) return ;
await vscode . commands . executeCommand ( "default:type" , { text : "\n" } ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function parentModule ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
return async ( ) = > {
2021-10-03 02:58:10 +00:00
const editor = vscode . window . activeTextEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2021-10-03 02:58:10 +00:00
if ( ! ( isRustDocument ( editor . document ) || isCargoTomlDocument ( editor . document ) ) ) return ;
2021-10-13 22:16:42 +00:00
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2022-10-17 12:20:14 +00:00
2021-03-15 14:49:20 +00:00
const locations = await client . sendRequest ( ra . parentModule , {
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier ( editor . document ) ,
2021-03-15 14:49:20 +00:00
position : client.code2ProtocolConverter.asPosition ( editor . selection . active ) ,
2020-05-25 09:10:31 +00:00
} ) ;
2021-10-13 22:16:42 +00:00
if ( ! locations ) return ;
2020-05-25 09:10:31 +00:00
2021-03-15 14:49:20 +00:00
if ( locations . length === 1 ) {
2023-06-27 19:03:53 +00:00
const loc = unwrapUndefinable ( locations [ 0 ] ) ;
2020-05-25 09:10:31 +00:00
2021-03-15 14:49:20 +00:00
const uri = client . protocol2CodeConverter . asUri ( loc . targetUri ) ;
const range = client . protocol2CodeConverter . asRange ( loc . targetRange ) ;
const doc = await vscode . workspace . openTextDocument ( uri ) ;
const e = await vscode . window . showTextDocument ( doc ) ;
e . selection = new vscode . Selection ( range . start , range . start ) ;
e . revealRange ( range , vscode . TextEditorRevealType . InCenter ) ;
} else {
const uri = editor . document . uri . toString ( ) ;
const position = client . code2ProtocolConverter . asPosition ( editor . selection . active ) ;
await showReferencesImpl (
client ,
uri ,
position ,
2023-07-11 13:35:10 +00:00
locations . map ( ( loc ) = > lc . Location . create ( loc . targetUri , loc . targetRange ) ) ,
2021-03-15 14:49:20 +00:00
) ;
}
2020-05-25 09:10:31 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function openCargoToml ( ctx : CtxInit ) : Cmd {
2020-11-13 01:48:07 +00:00
return async ( ) = > {
const editor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2020-11-13 01:48:07 +00:00
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-11-13 01:48:07 +00:00
const response = await client . sendRequest ( ra . openCargoToml , {
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier ( editor . document ) ,
2020-11-13 01:48:07 +00:00
} ) ;
if ( ! response ) return ;
const uri = client . protocol2CodeConverter . asUri ( response . uri ) ;
const range = client . protocol2CodeConverter . asRange ( response . range ) ;
const doc = await vscode . workspace . openTextDocument ( uri ) ;
const e = await vscode . window . showTextDocument ( doc ) ;
e . selection = new vscode . Selection ( range . start , range . start ) ;
e . revealRange ( range , vscode . TextEditorRevealType . InCenter ) ;
} ;
}
2022-02-26 00:37:55 +00:00
export function revealDependency ( ctx : CtxInit ) : Cmd {
return async ( editor : RustEditor ) = > {
2023-04-27 19:13:05 +00:00
if ( ! ctx . dependencies ? . isInitialized ( ) ) {
return ;
}
2022-02-26 00:37:55 +00:00
const documentPath = editor . document . uri . fsPath ;
2023-04-03 00:58:20 +00:00
const dep = ctx . dependencies ? . getDependency ( documentPath ) ;
2022-02-26 00:37:55 +00:00
if ( dep ) {
2023-04-03 00:58:20 +00:00
await ctx . treeView ? . reveal ( dep , { select : true , expand : true } ) ;
2022-02-26 00:37:55 +00:00
} else {
2023-04-05 17:57:01 +00:00
await revealParentChain ( editor . document , ctx ) ;
2022-02-26 00:37:55 +00:00
}
} ;
}
2023-04-05 17:57:01 +00:00
/ * *
* This function calculates the parent chain of a given file until it reaches it crate root contained in ctx . dependencies .
* This is need because the TreeView is Lazy , so at first it only has the root dependencies : For example if we have the following crates :
* - core
* - alloc
* - std
*
* if I want to reveal alloc / src / str . rs , I have to :
* 1 . reveal every children of alloc
* - core
* - alloc \
* & emsp ; | - beches \
* & emsp ; | - src \
* & emsp ; | - . . .
* - std
* 2 . reveal every children of src :
* core
* alloc \
* & emsp ; | - beches \
* & emsp ; | - src \
* & emsp ; & emsp ; | - lib . rs \
* & emsp ; & emsp ; | - str . rs < -- -- -- - FOUND IT ! \
* & emsp ; & emsp ; | - . . . \
* & emsp ; | - . . . \
* std
* /
async function revealParentChain ( document : RustDocument , ctx : CtxInit ) {
let documentPath = document . uri . fsPath ;
const maxDepth = documentPath . split ( path . sep ) . length - 1 ;
const parentChain : DependencyId [ ] = [ { id : documentPath.toLowerCase ( ) } ] ;
do {
documentPath = path . dirname ( documentPath ) ;
parentChain . push ( { id : documentPath.toLowerCase ( ) } ) ;
if ( parentChain . length >= maxDepth ) {
// this is an odd case that can happen when we change a crate version but we'd still have
// a open file referencing the old version
return ;
}
} while ( ! ctx . dependencies ? . contains ( documentPath ) ) ;
parentChain . reverse ( ) ;
for ( const idx in parentChain ) {
2023-06-27 19:03:53 +00:00
const treeView = ctx . treeView ;
if ( ! treeView ) {
continue ;
}
const dependency = unwrapUndefinable ( parentChain [ idx ] ) ;
await treeView . reveal ( dependency , { select : true , expand : true } ) ;
2023-04-05 17:57:01 +00:00
}
}
2022-02-26 00:37:55 +00:00
export async function execRevealDependency ( e : RustEditor ) : Promise < void > {
2022-07-17 15:38:56 +00:00
await vscode . commands . executeCommand ( "rust-analyzer.revealDependency" , e ) ;
2022-02-26 00:37:55 +00:00
}
2022-10-28 22:44:37 +00:00
export function ssr ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
return async ( ) = > {
2020-07-22 05:00:28 +00:00
const editor = vscode . window . activeTextEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-07-22 05:00:28 +00:00
const position = editor . selection . active ;
2020-07-29 01:44:01 +00:00
const selections = editor . selections ;
2022-10-17 12:20:14 +00:00
const textDocument = client . code2ProtocolConverter . asTextDocumentIdentifier (
2023-07-11 13:35:10 +00:00
editor . document ,
2020-09-29 20:05:18 +00:00
) ;
2020-05-25 09:10:31 +00:00
const options : vscode.InputBoxOptions = {
value : "() ==>> ()" ,
2020-09-15 11:32:56 +00:00
prompt : "Enter request, for example 'Foo($a) ==>> Foo::new($a)' " ,
2020-05-25 09:10:31 +00:00
validateInput : async ( x : string ) = > {
try {
2020-07-22 05:00:28 +00:00
await client . sendRequest ( ra . ssr , {
2020-07-29 01:44:01 +00:00
query : x ,
parseOnly : true ,
textDocument ,
position ,
selections ,
2020-07-22 05:00:28 +00:00
} ) ;
2020-05-25 09:10:31 +00:00
} catch ( e ) {
2023-07-10 14:03:08 +00:00
return String ( e ) ;
2020-05-25 09:10:31 +00:00
}
return null ;
} ,
} ;
const request = await vscode . window . showInputBox ( options ) ;
if ( ! request ) return ;
2021-02-09 14:12:46 +00:00
await vscode . window . withProgress (
{
2020-06-29 09:17:35 +00:00
location : vscode.ProgressLocation.Notification ,
title : "Structured search replace in progress..." ,
cancellable : false ,
2022-04-08 11:24:28 +00:00
} ,
async ( _progress , token ) = > {
2020-07-22 05:00:28 +00:00
const edit = await client . sendRequest ( ra . ssr , {
2020-07-29 01:44:01 +00:00
query : request ,
parseOnly : false ,
textDocument ,
position ,
selections ,
2020-07-22 05:00:28 +00:00
} ) ;
2022-05-17 17:15:06 +00:00
2022-04-08 11:24:28 +00:00
await vscode . workspace . applyEdit (
2023-07-11 13:35:10 +00:00
await client . protocol2CodeConverter . asWorkspaceEdit ( edit , token ) ,
2022-04-08 11:24:28 +00:00
) ;
2023-07-11 13:35:10 +00:00
} ,
2021-02-09 14:12:46 +00:00
) ;
2020-05-25 09:10:31 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function serverVersion ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
return async ( ) = > {
2022-10-17 12:53:46 +00:00
if ( ! ctx . serverPath ) {
void vscode . window . showWarningMessage ( ` rust-analyzer server is not running ` ) ;
return ;
}
2020-05-25 09:10:31 +00:00
const { stdout } = spawnSync ( ctx . serverPath , [ "--version" ] , { encoding : "utf8" } ) ;
2021-03-12 17:49:00 +00:00
const versionString = stdout . slice ( ` rust-analyzer ` . length ) . trim ( ) ;
2020-05-25 09:10:31 +00:00
void vscode . window . showInformationMessage ( ` rust-analyzer version: ${ versionString } ` ) ;
} ;
}
2020-05-25 10:02:30 +00:00
// Opens the virtual file that will show the syntax tree
//
// The contents of the file come from the `TextDocumentContentProvider`
2022-10-28 22:44:37 +00:00
export function syntaxTree ( ctx : CtxInit ) : Cmd {
2020-05-25 10:02:30 +00:00
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
2022-07-06 15:36:42 +00:00
readonly uri = vscode . Uri . parse ( "rust-analyzer-syntax-tree://syntaxtree/tree.rast" ) ;
2020-05-25 10:02:30 +00:00
readonly eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
constructor ( ) {
vscode . workspace . onDidChangeTextDocument (
this . onDidChangeTextDocument ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2020-05-25 10:02:30 +00:00
) ;
vscode . window . onDidChangeActiveTextEditor (
this . onDidChangeActiveTextEditor ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2020-05-25 10:02:30 +00:00
) ;
}
private onDidChangeTextDocument ( event : vscode.TextDocumentChangeEvent ) {
if ( isRustDocument ( event . document ) ) {
// We need to order this after language server updates, but there's no API for that.
// Hence, good old sleep().
void sleep ( 10 ) . then ( ( ) = > this . eventEmitter . fire ( this . uri ) ) ;
}
}
private onDidChangeActiveTextEditor ( editor : vscode.TextEditor | undefined ) {
if ( editor && isRustEditor ( editor ) ) {
this . eventEmitter . fire ( this . uri ) ;
}
}
2022-10-17 12:20:14 +00:00
async provideTextDocumentContent (
2020-05-25 10:02:30 +00:00
uri : vscode.Uri ,
2023-07-11 13:35:10 +00:00
ct : vscode.CancellationToken ,
2022-10-17 12:20:14 +00:00
) : Promise < string > {
2020-05-25 10:02:30 +00:00
const rustEditor = ctx . activeRustEditor ;
if ( ! rustEditor ) return "" ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-05-25 10:02:30 +00:00
// When the range based query is enabled we take the range of the selection
const range =
uri . query === "range=true" && ! rustEditor . selection . isEmpty
2022-10-17 12:20:14 +00:00
? client . code2ProtocolConverter . asRange ( rustEditor . selection )
2020-05-25 10:02:30 +00:00
: null ;
const params = { textDocument : { uri : rustEditor.document.uri.toString ( ) } , range } ;
2022-10-17 12:20:14 +00:00
return client . sendRequest ( ra . syntaxTree , params , ct ) ;
2020-05-25 10:02:30 +00:00
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2022-10-17 13:05:20 +00:00
ctx . pushExtCleanup ( new AstInspector ( ctx ) ) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2023-07-11 13:35:10 +00:00
vscode . workspace . registerTextDocumentContentProvider ( "rust-analyzer-syntax-tree" , tdcp ) ,
2022-07-06 15:36:42 +00:00
) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2020-05-25 10:02:30 +00:00
vscode . languages . setLanguageConfiguration ( "ra_syntax_tree" , {
brackets : [ [ "[" , ")" ] ] ,
2023-07-11 13:35:10 +00:00
} ) ,
2020-05-25 10:02:30 +00:00
) ;
return async ( ) = > {
const editor = vscode . window . activeTextEditor ;
const rangeEnabled = ! ! editor && ! editor . selection . isEmpty ;
const uri = rangeEnabled ? vscode . Uri . parse ( ` ${ tdcp . uri . toString ( ) } ?range=true ` ) : tdcp . uri ;
const document = await vscode . workspace . openTextDocument ( uri ) ;
tdcp . eventEmitter . fire ( uri ) ;
void ( await vscode . window . showTextDocument ( document , {
viewColumn : vscode.ViewColumn.Two ,
preserveFocus : true ,
} ) ) ;
} ;
}
2023-02-26 12:34:41 +00:00
function viewHirOrMir ( ctx : CtxInit , xir : "hir" | "mir" ) : Cmd {
const viewXir = xir === "hir" ? "viewHir" : "viewMir" ;
const requestType = xir === "hir" ? ra.viewHir : ra.viewMir ;
2023-04-28 17:14:30 +00:00
const uri = ` rust-analyzer- ${ xir } :// ${ viewXir } / ${ xir } .rs ` ;
const scheme = ` rust-analyzer- ${ xir } ` ;
return viewFileUsingTextDocumentContentProvider ( ctx , requestType , uri , scheme , true ) ;
}
function viewFileUsingTextDocumentContentProvider (
ctx : CtxInit ,
requestType : lc.RequestType < lc.TextDocumentPositionParams , string , void > ,
uri : string ,
scheme : string ,
2023-07-11 13:35:10 +00:00
shouldUpdate : boolean ,
2023-04-28 17:14:30 +00:00
) : Cmd {
2020-12-28 18:29:58 +00:00
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
2023-04-28 17:14:30 +00:00
readonly uri = vscode . Uri . parse ( uri ) ;
2020-12-28 18:29:58 +00:00
readonly eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
constructor ( ) {
vscode . workspace . onDidChangeTextDocument (
this . onDidChangeTextDocument ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2020-12-28 18:29:58 +00:00
) ;
vscode . window . onDidChangeActiveTextEditor (
this . onDidChangeActiveTextEditor ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2020-12-28 18:29:58 +00:00
) ;
}
private onDidChangeTextDocument ( event : vscode.TextDocumentChangeEvent ) {
2023-04-28 17:14:30 +00:00
if ( isRustDocument ( event . document ) && shouldUpdate ) {
2020-12-28 18:29:58 +00:00
// We need to order this after language server updates, but there's no API for that.
// Hence, good old sleep().
void sleep ( 10 ) . then ( ( ) = > this . eventEmitter . fire ( this . uri ) ) ;
}
}
private onDidChangeActiveTextEditor ( editor : vscode.TextEditor | undefined ) {
2023-04-28 17:14:30 +00:00
if ( editor && isRustEditor ( editor ) && shouldUpdate ) {
2020-12-28 18:29:58 +00:00
this . eventEmitter . fire ( this . uri ) ;
}
}
2022-10-17 12:20:14 +00:00
async provideTextDocumentContent (
2020-12-28 18:29:58 +00:00
_uri : vscode.Uri ,
2023-07-11 13:35:10 +00:00
ct : vscode.CancellationToken ,
2022-10-17 12:20:14 +00:00
) : Promise < string > {
2020-12-28 18:29:58 +00:00
const rustEditor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! rustEditor ) return "" ;
2020-12-28 18:29:58 +00:00
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-12-28 18:29:58 +00:00
const params = {
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier (
2023-07-11 13:35:10 +00:00
rustEditor . document ,
2020-12-28 18:29:58 +00:00
) ,
2021-03-15 14:49:20 +00:00
position : client.code2ProtocolConverter.asPosition ( rustEditor . selection . active ) ,
2020-12-28 18:29:58 +00:00
} ;
2023-02-26 12:34:41 +00:00
return client . sendRequest ( requestType , params , ct ) ;
2020-12-28 18:29:58 +00:00
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2023-04-28 17:14:30 +00:00
ctx . pushExtCleanup ( vscode . workspace . registerTextDocumentContentProvider ( scheme , tdcp ) ) ;
2022-03-31 12:50:33 +00:00
return async ( ) = > {
const document = await vscode . workspace . openTextDocument ( tdcp . uri ) ;
tdcp . eventEmitter . fire ( tdcp . uri ) ;
void ( await vscode . window . showTextDocument ( document , {
viewColumn : vscode.ViewColumn.Two ,
preserveFocus : true ,
} ) ) ;
} ;
}
2023-02-26 12:34:41 +00:00
// Opens the virtual file that will show the HIR of the function containing the cursor position
//
// The contents of the file come from the `TextDocumentContentProvider`
export function viewHir ( ctx : CtxInit ) : Cmd {
return viewHirOrMir ( ctx , "hir" ) ;
}
// Opens the virtual file that will show the MIR of the function containing the cursor position
//
// The contents of the file come from the `TextDocumentContentProvider`
export function viewMir ( ctx : CtxInit ) : Cmd {
return viewHirOrMir ( ctx , "mir" ) ;
}
2023-04-28 17:14:30 +00:00
// Opens the virtual file that will show the MIR of the function containing the cursor position
//
// The contents of the file come from the `TextDocumentContentProvider`
export function interpretFunction ( ctx : CtxInit ) : Cmd {
const uri = ` rust-analyzer-interpret-function://interpretFunction/result.log ` ;
return viewFileUsingTextDocumentContentProvider (
ctx ,
ra . interpretFunction ,
uri ,
` rust-analyzer-interpret-function ` ,
2023-07-11 13:35:10 +00:00
false ,
2023-04-28 17:14:30 +00:00
) ;
}
2022-10-28 22:44:37 +00:00
export function viewFileText ( ctx : CtxInit ) : Cmd {
2022-03-31 12:50:33 +00:00
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
2022-07-06 15:36:42 +00:00
readonly uri = vscode . Uri . parse ( "rust-analyzer-file-text://viewFileText/file.rs" ) ;
2022-03-31 12:50:33 +00:00
readonly eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
constructor ( ) {
vscode . workspace . onDidChangeTextDocument (
this . onDidChangeTextDocument ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2022-03-31 12:50:33 +00:00
) ;
vscode . window . onDidChangeActiveTextEditor (
this . onDidChangeActiveTextEditor ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2022-03-31 12:50:33 +00:00
) ;
}
private onDidChangeTextDocument ( event : vscode.TextDocumentChangeEvent ) {
if ( isRustDocument ( event . document ) ) {
// We need to order this after language server updates, but there's no API for that.
// Hence, good old sleep().
void sleep ( 10 ) . then ( ( ) = > this . eventEmitter . fire ( this . uri ) ) ;
}
}
private onDidChangeActiveTextEditor ( editor : vscode.TextEditor | undefined ) {
if ( editor && isRustEditor ( editor ) ) {
this . eventEmitter . fire ( this . uri ) ;
}
}
2022-10-17 12:20:14 +00:00
async provideTextDocumentContent (
2022-03-31 12:50:33 +00:00
_uri : vscode.Uri ,
2023-07-11 13:35:10 +00:00
ct : vscode.CancellationToken ,
2022-10-17 12:20:14 +00:00
) : Promise < string > {
2022-03-31 12:50:33 +00:00
const rustEditor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! rustEditor ) return "" ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2022-03-31 12:50:33 +00:00
const params = client . code2ProtocolConverter . asTextDocumentIdentifier (
2023-07-11 13:35:10 +00:00
rustEditor . document ,
2022-03-31 12:50:33 +00:00
) ;
return client . sendRequest ( ra . viewFileText , params , ct ) ;
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2023-07-11 13:35:10 +00:00
vscode . workspace . registerTextDocumentContentProvider ( "rust-analyzer-file-text" , tdcp ) ,
2022-07-06 15:36:42 +00:00
) ;
2020-12-28 18:29:58 +00:00
return async ( ) = > {
2021-01-01 19:35:10 +00:00
const document = await vscode . workspace . openTextDocument ( tdcp . uri ) ;
tdcp . eventEmitter . fire ( tdcp . uri ) ;
2020-12-28 18:29:58 +00:00
void ( await vscode . window . showTextDocument ( document , {
viewColumn : vscode.ViewColumn.Two ,
preserveFocus : true ,
} ) ) ;
} ;
}
2020-05-25 10:02:30 +00:00
2022-10-28 22:44:37 +00:00
export function viewItemTree ( ctx : CtxInit ) : Cmd {
2021-05-21 21:59:52 +00:00
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
2022-07-06 15:36:42 +00:00
readonly uri = vscode . Uri . parse ( "rust-analyzer-item-tree://viewItemTree/itemtree.rs" ) ;
2021-05-21 21:59:52 +00:00
readonly eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
constructor ( ) {
vscode . workspace . onDidChangeTextDocument (
this . onDidChangeTextDocument ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2021-05-21 21:59:52 +00:00
) ;
vscode . window . onDidChangeActiveTextEditor (
this . onDidChangeActiveTextEditor ,
this ,
2023-07-11 13:35:10 +00:00
ctx . subscriptions ,
2021-05-21 21:59:52 +00:00
) ;
}
private onDidChangeTextDocument ( event : vscode.TextDocumentChangeEvent ) {
if ( isRustDocument ( event . document ) ) {
// We need to order this after language server updates, but there's no API for that.
// Hence, good old sleep().
void sleep ( 10 ) . then ( ( ) = > this . eventEmitter . fire ( this . uri ) ) ;
}
}
private onDidChangeActiveTextEditor ( editor : vscode.TextEditor | undefined ) {
if ( editor && isRustEditor ( editor ) ) {
this . eventEmitter . fire ( this . uri ) ;
}
}
2022-10-17 12:20:14 +00:00
async provideTextDocumentContent (
2021-05-21 21:59:52 +00:00
_uri : vscode.Uri ,
2023-07-11 13:35:10 +00:00
ct : vscode.CancellationToken ,
2022-10-17 12:20:14 +00:00
) : Promise < string > {
2021-05-21 21:59:52 +00:00
const rustEditor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! rustEditor ) return "" ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2021-05-21 21:59:52 +00:00
const params = {
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier (
2023-07-11 13:35:10 +00:00
rustEditor . document ,
2021-05-21 21:59:52 +00:00
) ,
} ;
return client . sendRequest ( ra . viewItemTree , params , ct ) ;
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2023-07-11 13:35:10 +00:00
vscode . workspace . registerTextDocumentContentProvider ( "rust-analyzer-item-tree" , tdcp ) ,
2022-07-06 15:36:42 +00:00
) ;
2021-05-21 21:59:52 +00:00
return async ( ) = > {
const document = await vscode . workspace . openTextDocument ( tdcp . uri ) ;
tdcp . eventEmitter . fire ( tdcp . uri ) ;
void ( await vscode . window . showTextDocument ( document , {
viewColumn : vscode.ViewColumn.Two ,
preserveFocus : true ,
} ) ) ;
} ;
}
2022-10-28 22:44:37 +00:00
function crateGraph ( ctx : CtxInit , full : boolean ) : Cmd {
2021-05-11 14:15:31 +00:00
return async ( ) = > {
2021-08-09 17:45:42 +00:00
const nodeModulesPath = vscode . Uri . file ( path . join ( ctx . extensionPath , "node_modules" ) ) ;
2021-08-06 17:14:47 +00:00
2021-08-10 13:34:30 +00:00
const panel = vscode . window . createWebviewPanel (
"rust-analyzer.crate-graph" ,
"rust-analyzer crate graph" ,
vscode . ViewColumn . Two ,
{
enableScripts : true ,
2021-08-10 13:35:37 +00:00
retainContextWhenHidden : true ,
2021-08-09 17:45:42 +00:00
localResourceRoots : [ nodeModulesPath ] ,
2023-07-11 13:35:10 +00:00
} ,
2021-08-10 13:35:37 +00:00
) ;
2021-07-01 22:08:05 +00:00
const params = {
full : full ,
} ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2022-10-17 12:20:14 +00:00
const dot = await client . sendRequest ( ra . viewCrateGraph , params ) ;
2021-08-10 12:42:50 +00:00
const uri = panel . webview . asWebviewUri ( nodeModulesPath ) ;
2021-08-10 13:35:37 +00:00
2021-08-10 13:34:30 +00:00
const html = `
2021-08-09 07:01:42 +00:00
< ! DOCTYPE html >
< meta charset = "utf-8" >
< head >
< style >
/* Fill the entire view */
html , body { margin :0 ; padding :0 ; overflow :hidden }
svg { position :fixed ; top :0 ; left :0 ; height :100 % ; width :100 % }
2022-08-17 13:44:58 +00:00
/* Disable the graphviz background and fill the polygons */
2021-08-09 07:01:42 +00:00
. graph > polygon { display :none ; }
: is ( . node , . edge ) polygon { fill : white ; }
/* Invert the line colours for dark themes */
body :not ( . vscode - light ) . edge path { stroke : white ; }
< / style >
< / head >
< body >
2021-08-10 12:42:50 +00:00
< script type = "text/javascript" src = "${uri}/d3/dist/d3.min.js" > < / script >
2023-04-13 15:38:12 +00:00
< script type = "text/javascript" src = "${uri}/@hpcc-js/wasm/dist/graphviz.umd.js" > < / script >
2021-08-10 12:42:50 +00:00
< script type = "text/javascript" src = "${uri}/d3-graphviz/build/d3-graphviz.min.js" > < / script >
2021-08-09 07:01:42 +00:00
< div id = "graph" > < / div >
< script >
2023-04-13 20:01:57 +00:00
let dot = \ ` ${ dot } \` ;
2021-08-09 07:01:42 +00:00
let graph = d3 . select ( "#graph" )
2023-04-13 15:38:12 +00:00
. graphviz ( { useWorker : false , useSharedWorker : false } )
2021-08-09 07:01:42 +00:00
. fit ( true )
. zoomScaleExtent ( [ 0.1 , Infinity ] )
2023-04-13 20:01:57 +00:00
. renderDot ( dot ) ;
2021-08-09 07:01:42 +00:00
d3 . select ( window ) . on ( "click" , ( event ) = > {
if ( event . ctrlKey ) {
graph . resetZoom ( d3 . transition ( ) . duration ( 100 ) ) ;
}
} ) ;
2023-04-13 20:01:57 +00:00
d3 . select ( window ) . on ( "copy" , ( event ) = > {
event . clipboardData . setData ( "text/plain" , dot ) ;
event . preventDefault ( ) ;
} ) ;
2021-08-09 07:01:42 +00:00
< / script >
< / body >
` ;
2021-08-10 13:34:30 +00:00
panel . webview . html = html ;
2021-05-11 14:15:31 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function viewCrateGraph ( ctx : CtxInit ) : Cmd {
2021-07-01 22:08:05 +00:00
return crateGraph ( ctx , false ) ;
}
2022-10-28 22:44:37 +00:00
export function viewFullCrateGraph ( ctx : CtxInit ) : Cmd {
2021-07-01 22:08:05 +00:00
return crateGraph ( ctx , true ) ;
}
2020-05-25 09:10:31 +00:00
// Opens the virtual file that will show the syntax tree
//
// The contents of the file come from the `TextDocumentContentProvider`
2022-10-28 22:44:37 +00:00
export function expandMacro ( ctx : CtxInit ) : Cmd {
2020-05-25 09:10:31 +00:00
function codeFormat ( expanded : ra.ExpandedMacro ) : string {
2023-04-02 10:45:39 +00:00
let result = ` // Recursive expansion of ${ expanded . name } macro \ n ` ;
2020-05-25 09:10:31 +00:00
result += "// " + "=" . repeat ( result . length - 3 ) ;
result += "\n\n" ;
result += expanded . expansion ;
return result ;
}
const tdcp = new ( class implements vscode . TextDocumentContentProvider {
2022-07-06 15:36:42 +00:00
uri = vscode . Uri . parse ( "rust-analyzer-expand-macro://expandMacro/[EXPANSION].rs" ) ;
2020-05-25 09:10:31 +00:00
eventEmitter = new vscode . EventEmitter < vscode.Uri > ( ) ;
async provideTextDocumentContent ( _uri : vscode.Uri ) : Promise < string > {
const editor = vscode . window . activeTextEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return "" ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-05-25 09:10:31 +00:00
const position = editor . selection . active ;
const expanded = await client . sendRequest ( ra . expandMacro , {
2022-10-17 12:20:14 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier (
2023-07-11 13:35:10 +00:00
editor . document ,
2020-09-29 20:05:18 +00:00
) ,
2020-05-25 09:10:31 +00:00
position ,
} ) ;
if ( expanded == null ) return "Not available" ;
return codeFormat ( expanded ) ;
}
get onDidChange ( ) : vscode . Event < vscode.Uri > {
return this . eventEmitter . event ;
}
} ) ( ) ;
2022-10-17 12:20:14 +00:00
ctx . pushExtCleanup (
2023-07-11 13:35:10 +00:00
vscode . workspace . registerTextDocumentContentProvider ( "rust-analyzer-expand-macro" , tdcp ) ,
2022-07-06 15:36:42 +00:00
) ;
2020-05-25 09:10:31 +00:00
return async ( ) = > {
const document = await vscode . workspace . openTextDocument ( tdcp . uri ) ;
tdcp . eventEmitter . fire ( tdcp . uri ) ;
return vscode . window . showTextDocument ( document , vscode . ViewColumn . Two , true ) ;
} ;
}
2020-02-02 19:37:22 +00:00
2022-10-28 22:44:37 +00:00
export function reloadWorkspace ( ctx : CtxInit ) : Cmd {
return async ( ) = > ctx . client . sendRequest ( ra . reloadWorkspace ) ;
2019-12-30 13:53:43 +00:00
}
2023-03-26 06:39:28 +00:00
export function rebuildProcMacros ( ctx : CtxInit ) : Cmd {
return async ( ) = > ctx . client . sendRequest ( ra . rebuildProcMacros ) ;
2023-03-25 15:47:41 +00:00
}
2021-02-27 17:04:43 +00:00
async function showReferencesImpl (
2022-10-17 12:20:14 +00:00
client : LanguageClient | undefined ,
2021-02-27 17:04:43 +00:00
uri : string ,
position : lc.Position ,
2023-07-11 13:35:10 +00:00
locations : lc.Location [ ] ,
2021-02-27 17:04:43 +00:00
) {
if ( client ) {
await vscode . commands . executeCommand (
"editor.action.showReferences" ,
vscode . Uri . parse ( uri ) ,
client . protocol2CodeConverter . asPosition ( position ) ,
2023-07-11 13:35:10 +00:00
locations . map ( client . protocol2CodeConverter . asLocation ) ,
2021-02-27 17:04:43 +00:00
) ;
}
}
2022-10-28 22:44:37 +00:00
export function showReferences ( ctx : CtxInit ) : Cmd {
2021-02-09 14:12:46 +00:00
return async ( uri : string , position : lc.Position , locations : lc.Location [ ] ) = > {
2022-10-28 22:44:37 +00:00
await showReferencesImpl ( ctx . client , uri , position , locations ) ;
2019-12-30 19:07:04 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function applyActionGroup ( _ctx : CtxInit ) : Cmd {
2020-11-10 17:20:01 +00:00
return async ( actions : { label : string ; arguments : lc.CodeAction } [ ] ) = > {
2020-05-22 15:29:55 +00:00
const selectedAction = await vscode . window . showQuickPick ( actions ) ;
if ( ! selectedAction ) return ;
2021-02-09 14:12:46 +00:00
await vscode . commands . executeCommand (
2020-06-02 20:21:48 +00:00
"rust-analyzer.resolveCodeAction" ,
2023-07-11 13:35:10 +00:00
selectedAction . arguments ,
2021-02-09 14:12:46 +00:00
) ;
2020-06-02 20:21:48 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function gotoLocation ( ctx : CtxInit ) : Cmd {
2020-06-10 20:01:19 +00:00
return async ( locationLink : lc.LocationLink ) = > {
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2022-10-17 12:20:14 +00:00
const uri = client . protocol2CodeConverter . asUri ( locationLink . targetUri ) ;
let range = client . protocol2CodeConverter . asRange ( locationLink . targetSelectionRange ) ;
// collapse the range to a cursor position
range = range . with ( { end : range.start } ) ;
2020-06-10 20:01:19 +00:00
2022-10-17 12:20:14 +00:00
await vscode . window . showTextDocument ( uri , { selection : range } ) ;
2020-06-10 20:01:19 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function openDocs ( ctx : CtxInit ) : Cmd {
2020-08-30 08:02:29 +00:00
return async ( ) = > {
const editor = vscode . window . activeTextEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) {
2020-09-03 07:55:24 +00:00
return ;
2020-08-30 08:02:29 +00:00
}
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-08-30 08:02:29 +00:00
const position = editor . selection . active ;
const textDocument = { uri : editor.document.uri.toString ( ) } ;
2023-10-18 12:02:56 +00:00
const docLinks = await client . sendRequest ( ra . openDocs , { position , textDocument } ) ;
log . debug ( docLinks ) ;
2023-10-08 01:52:15 +00:00
let fileType = vscode . FileType . Unknown ;
2023-10-18 12:02:56 +00:00
if ( docLinks . local !== undefined ) {
2023-10-08 01:52:15 +00:00
try {
2023-10-18 12:02:56 +00:00
fileType = ( await vscode . workspace . fs . stat ( vscode . Uri . parse ( docLinks . local ) ) ) . type ;
2023-10-08 01:52:15 +00:00
} catch ( e ) {
log . debug ( "stat() threw error. Falling back to web version" , e ) ;
}
}
2023-10-18 12:02:56 +00:00
let docLink = fileType & vscode . FileType . File ? docLinks.local : docLinks.web ;
if ( docLink ) {
// instruct vscode to handle the vscode-remote link directly
if ( docLink . startsWith ( "vscode-remote://" ) ) {
docLink = docLink . replace ( "vscode-remote://" , "vscode://vscode-remote/" ) ;
}
const docUri = vscode . Uri . parse ( docLink ) ;
await vscode . env . openExternal ( docUri ) ;
}
} ;
}
export function openExternalDocs ( ctx : CtxInit ) : Cmd {
return async ( ) = > {
const editor = vscode . window . activeTextEditor ;
if ( ! editor ) {
return ;
2023-10-08 01:52:15 +00:00
}
2023-10-18 12:02:56 +00:00
const client = ctx . client ;
const position = editor . selection . active ;
const textDocument = { uri : editor.document.uri.toString ( ) } ;
2020-08-30 08:02:29 +00:00
2023-10-18 12:02:56 +00:00
const docLinks = await client . sendRequest ( ra . openDocs , { position , textDocument } ) ;
let docLink = docLinks . web ;
if ( docLink ) {
// instruct vscode to handle the vscode-remote link directly
if ( docLink . startsWith ( "vscode-remote://" ) ) {
docLink = docLink . replace ( "vscode-remote://" , "vscode://vscode-remote/" ) ;
}
const docUri = vscode . Uri . parse ( docLink ) ;
await vscode . env . openExternal ( docUri ) ;
2020-09-01 08:26:10 +00:00
}
2020-08-30 08:02:29 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function cancelFlycheck ( ctx : CtxInit ) : Cmd {
2022-08-19 06:52:31 +00:00
return async ( ) = > {
2022-12-17 22:29:25 +00:00
await ctx . client . sendNotification ( ra . cancelFlycheck ) ;
2022-12-16 21:43:14 +00:00
} ;
}
2022-12-17 22:43:26 +00:00
export function clearFlycheck ( ctx : CtxInit ) : Cmd {
return async ( ) = > {
await ctx . client . sendNotification ( ra . clearFlycheck ) ;
} ;
}
2022-12-16 21:43:14 +00:00
export function runFlycheck ( ctx : CtxInit ) : Cmd {
return async ( ) = > {
const editor = ctx . activeRustEditor ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2022-12-16 21:43:14 +00:00
const params = editor ? { uri : editor.document.uri.toString ( ) } : null ;
await client . sendNotification ( ra . runFlycheck , { textDocument : params } ) ;
2022-08-19 06:52:31 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function resolveCodeAction ( ctx : CtxInit ) : Cmd {
2020-11-10 17:20:01 +00:00
return async ( params : lc.CodeAction ) = > {
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2020-11-10 17:20:01 +00:00
params . command = undefined ;
2022-12-16 21:43:14 +00:00
const item = await client . sendRequest ( lc . CodeActionResolveRequest . type , params ) ;
2022-10-17 12:20:14 +00:00
if ( ! item ? . edit ) {
2020-06-02 20:21:48 +00:00
return ;
}
2020-12-29 14:43:17 +00:00
const itemEdit = item . edit ;
// filter out all text edits and recreate the WorkspaceEdit without them so we can apply
// snippet edits on our own
2021-02-05 20:26:14 +00:00
const lcFileSystemEdit = {
. . . itemEdit ,
documentChanges : itemEdit.documentChanges?.filter ( ( change ) = > "kind" in change ) ,
} ;
2022-04-08 11:24:28 +00:00
const fileSystemEdit = await client . protocol2CodeConverter . asWorkspaceEdit (
2023-07-11 13:35:10 +00:00
lcFileSystemEdit ,
2022-04-08 11:24:28 +00:00
) ;
2021-02-05 20:26:14 +00:00
await vscode . workspace . applyEdit ( fileSystemEdit ) ;
2024-02-02 01:38:42 +00:00
// replace all text edits so that we can convert snippet text edits into `vscode.SnippetTextEdit`s
// FIXME: this is a workaround until vscode-languageclient supports doing the SnippeTextEdit conversion itself
// also need to carry the snippetTextDocumentEdits separately, since we can't retrieve them again using WorkspaceEdit.entries
const [ workspaceTextEdit , snippetTextDocumentEdits ] = asWorkspaceSnippetEdit ( ctx , itemEdit ) ;
await applySnippetWorkspaceEdit ( workspaceTextEdit , snippetTextDocumentEdits ) ;
2022-04-19 16:37:18 +00:00
if ( item . command != null ) {
await vscode . commands . executeCommand ( item . command . command , item . command . arguments ) ;
}
2019-12-31 17:55:34 +00:00
} ;
2019-12-30 22:45:50 +00:00
}
2020-05-17 23:53:55 +00:00
2024-02-02 01:38:42 +00:00
function asWorkspaceSnippetEdit (
ctx : CtxInit ,
item : lc.WorkspaceEdit ,
) : [ vscode . WorkspaceEdit , SnippetTextDocumentEdit [ ] ] {
const client = ctx . client ;
// partially borrowed from https://github.com/microsoft/vscode-languageserver-node/blob/295aaa393fda8ecce110c38880a00466b9320e63/client/src/common/protocolConverter.ts#L1060-L1101
const result = new vscode . WorkspaceEdit ( ) ;
if ( item . documentChanges ) {
const snippetTextDocumentEdits : SnippetTextDocumentEdit [ ] = [ ] ;
for ( const change of item . documentChanges ) {
if ( lc . TextDocumentEdit . is ( change ) ) {
const uri = client . protocol2CodeConverter . asUri ( change . textDocument . uri ) ;
const snippetTextEdits : ( vscode . TextEdit | vscode . SnippetTextEdit ) [ ] = [ ] ;
for ( const edit of change . edits ) {
if (
"insertTextFormat" in edit &&
edit . insertTextFormat === lc . InsertTextFormat . Snippet
) {
// is a snippet text edit
snippetTextEdits . push (
new vscode . SnippetTextEdit (
client . protocol2CodeConverter . asRange ( edit . range ) ,
new vscode . SnippetString ( edit . newText ) ,
) ,
) ;
} else {
// always as a text document edit
snippetTextEdits . push (
vscode . TextEdit . replace (
client . protocol2CodeConverter . asRange ( edit . range ) ,
edit . newText ,
) ,
) ;
}
}
snippetTextDocumentEdits . push ( [ uri , snippetTextEdits ] ) ;
}
}
return [ result , snippetTextDocumentEdits ] ;
} else {
// we don't handle WorkspaceEdit.changes since it's not relevant for code actions
return [ result , [ ] ] ;
}
}
2022-10-28 22:44:37 +00:00
export function applySnippetWorkspaceEditCommand ( _ctx : CtxInit ) : Cmd {
2020-05-17 23:53:55 +00:00
return async ( edit : vscode.WorkspaceEdit ) = > {
2024-02-02 01:38:42 +00:00
await applySnippetWorkspaceEdit ( edit , edit . entries ( ) ) ;
2020-05-21 12:26:44 +00:00
} ;
}
2020-06-02 12:33:47 +00:00
2022-10-28 22:44:37 +00:00
export function run ( ctx : CtxInit ) : Cmd {
2020-06-02 12:33:47 +00:00
let prevRunnable : RunnableQuickPick | undefined ;
return async ( ) = > {
const item = await selectRunnable ( ctx , prevRunnable ) ;
if ( ! item ) return ;
item . detail = "rerun" ;
prevRunnable = item ;
2020-06-18 19:20:13 +00:00
const task = await createTask ( item . runnable , ctx . config ) ;
2020-06-02 12:33:47 +00:00
return await vscode . tasks . executeTask ( task ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function peekTests ( ctx : CtxInit ) : Cmd {
2021-02-27 17:04:43 +00:00
return async ( ) = > {
const editor = ctx . activeRustEditor ;
2022-10-17 12:20:14 +00:00
if ( ! editor ) return ;
2022-10-28 22:44:37 +00:00
const client = ctx . client ;
2021-02-27 17:04:43 +00:00
2021-02-27 18:07:23 +00:00
await vscode . window . withProgress (
{
location : vscode.ProgressLocation.Notification ,
title : "Looking for tests..." ,
cancellable : false ,
} ,
async ( _progress , _token ) = > {
const uri = editor . document . uri . toString ( ) ;
const position = client . code2ProtocolConverter . asPosition ( editor . selection . active ) ;
2022-05-17 17:15:06 +00:00
2021-02-27 18:07:23 +00:00
const tests = await client . sendRequest ( ra . relatedTests , {
textDocument : { uri : uri } ,
position : position ,
} ) ;
const locations : lc.Location [ ] = tests . map ( ( it ) = >
lc . Location . create (
it . runnable . location ! . targetUri ,
2023-07-11 13:35:10 +00:00
it . runnable . location ! . targetSelectionRange ,
) ,
2021-02-27 18:07:23 +00:00
) ;
2022-05-17 17:15:06 +00:00
2021-02-27 18:07:23 +00:00
await showReferencesImpl ( client , uri , position , locations ) ;
2023-07-11 13:35:10 +00:00
} ,
2021-02-27 18:07:23 +00:00
) ;
2021-02-27 17:04:43 +00:00
} ;
}
2022-10-28 22:44:37 +00:00
export function runSingle ( ctx : CtxInit ) : Cmd {
2020-06-02 12:33:47 +00:00
return async ( runnable : ra.Runnable ) = > {
const editor = ctx . activeRustEditor ;
if ( ! editor ) return ;
2020-06-18 19:20:13 +00:00
const task = await createTask ( runnable , ctx . config ) ;
2020-06-02 12:33:47 +00:00
task . group = vscode . TaskGroup . Build ;
task . presentationOptions = {
reveal : vscode.TaskRevealKind.Always ,
panel : vscode.TaskPanelKind.Dedicated ,
clear : true ,
} ;
return vscode . tasks . executeTask ( task ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function copyRunCommandLine ( ctx : CtxInit ) {
2021-02-10 11:28:13 +00:00
let prevRunnable : RunnableQuickPick | undefined ;
return async ( ) = > {
const item = await selectRunnable ( ctx , prevRunnable ) ;
if ( ! item ) return ;
const args = createArgs ( item . runnable ) ;
const commandLine = [ "cargo" , . . . args ] . join ( " " ) ;
await vscode . env . clipboard . writeText ( commandLine ) ;
await vscode . window . showInformationMessage ( "Cargo invocation copied to the clipboard." ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function debug ( ctx : CtxInit ) : Cmd {
2020-06-02 12:33:47 +00:00
let prevDebuggee : RunnableQuickPick | undefined ;
return async ( ) = > {
const item = await selectRunnable ( ctx , prevDebuggee , true ) ;
if ( ! item ) return ;
item . detail = "restart" ;
prevDebuggee = item ;
return await startDebugSession ( ctx , item . runnable ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function debugSingle ( ctx : CtxInit ) : Cmd {
2020-06-02 12:33:47 +00:00
return async ( config : ra.Runnable ) = > {
await startDebugSession ( ctx , config ) ;
} ;
}
2022-10-28 22:44:37 +00:00
export function newDebugConfig ( ctx : CtxInit ) : Cmd {
2020-06-02 12:33:47 +00:00
return async ( ) = > {
const item = await selectRunnable ( ctx , undefined , true , false ) ;
if ( ! item ) return ;
await makeDebugConfig ( ctx , item . runnable ) ;
} ;
}
2022-05-16 18:53:00 +00:00
2022-10-17 12:20:14 +00:00
export function linkToCommand ( _ : Ctx ) : Cmd {
2022-05-16 18:53:00 +00:00
return async ( commandId : string ) = > {
const link = LINKED_COMMANDS . get ( commandId ) ;
2022-10-17 12:20:14 +00:00
if ( link ) {
2022-05-16 18:53:00 +00:00
const { command , arguments : args = [ ] } = link ;
await vscode . commands . executeCommand ( command , . . . args ) ;
}
} ;
}
2023-06-19 00:31:46 +00:00
export function viewMemoryLayout ( ctx : CtxInit ) : Cmd {
return async ( ) = > {
const editor = vscode . window . activeTextEditor ;
2023-06-19 04:27:53 +00:00
if ( ! editor ) return ;
2023-06-19 00:31:46 +00:00
const client = ctx . client ;
const position = editor . selection . active ;
const expanded = await client . sendRequest ( ra . viewRecursiveMemoryLayout , {
2023-06-19 04:24:42 +00:00
textDocument : client.code2ProtocolConverter.asTextDocumentIdentifier ( editor . document ) ,
2023-06-19 00:31:46 +00:00
position ,
} ) ;
const document = vscode . window . createWebviewPanel (
"memory_layout" ,
"[Memory Layout]" ,
vscode . ViewColumn . Two ,
2023-07-11 13:35:10 +00:00
{ enableScripts : true } ,
2023-06-19 04:24:42 +00:00
) ;
2023-06-19 00:31:46 +00:00
document . webview . html = ` <!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Document < / title >
2023-07-10 14:03:08 +00:00
< style >
2023-06-19 00:31:46 +00:00
* {
box - sizing : border - box ;
}
body {
margin : 0 ;
overflow : hidden ;
min - height : 100 % ;
height : 100vh ;
padding : 32px ;
position : relative ;
display : block ;
background - color : var ( -- vscode - editor - background ) ;
font - family : var ( -- vscode - editor - font - family ) ;
font - size : var ( -- vscode - editor - font - size ) ;
color : var ( -- vscode - editor - foreground ) ;
}
. container {
position : relative ;
}
. trans {
transition : all 0.2 s ease - in - out ;
}
. grid {
height : 100 % ;
position : relative ;
color : var ( -- vscode - commandCenter - activeBorder ) ;
pointer - events : none ;
}
. grid - line {
position : absolute ;
width : 100 % ;
height : 1px ;
background - color : var ( -- vscode - commandCenter - activeBorder ) ;
}
# tooltip {
position : fixed ;
display : none ;
z - index : 1 ;
pointer - events : none ;
padding : 4px 8 px ;
z - index : 2 ;
color : var ( -- vscode - editorHoverWidget - foreground ) ;
background - color : var ( -- vscode - editorHoverWidget - background ) ;
border : 1px solid var ( -- vscode - editorHoverWidget - border ) ;
}
# tooltip b {
color : var ( -- vscode - editorInlayHint - typeForeground ) ;
}
# tooltip ul {
margin - left : 0 ;
padding - left : 20px ;
}
table {
position : absolute ;
transform : rotateZ ( 90 deg ) rotateX ( 180 deg ) ;
transform - origin : top left ;
border - collapse : collapse ;
table - layout : fixed ;
left : 48px ;
top : 0 ;
max - height : calc ( 100 vw - 64 px - 48 px ) ;
z - index : 1 ;
}
td {
border : 1px solid var ( -- vscode - focusBorder ) ;
writing - mode : vertical - rl ;
text - orientation : sideways - right ;
height : 80px ;
}
td p {
height : calc ( 100 % - 16 px ) ;
width : calc ( 100 % - 8 px ) ;
margin : 8px 4 px ;
display : inline - block ;
transform : rotateY ( 180 deg ) ;
pointer - events : none ;
overflow : hidden ;
}
td p * {
overflow : hidden ;
white - space : nowrap ;
text - overflow : ellipsis ;
display : inline - block ;
height : 100 % ;
}
td p b {
color : var ( -- vscode - editorInlayHint - typeForeground ) ;
}
td :hover {
background - color : var ( -- vscode - editor - hoverHighlightBackground ) ;
}
td :empty {
visibility : hidden ;
border : 0 ;
}
< / style >
< / head >
< body >
< div id = "tooltip" > < / div >
< / body >
< script > ( function ( ) {
const data = $ { JSON . stringify ( expanded ) }
if ( ! ( data && data . nodes . length ) ) {
document . body . innerText = "Not Available"
return
}
data . nodes . map ( n = > {
n . typename = n . typename . replaceAll ( '&' , '&' ) . replaceAll ( '<' , '<' ) . replaceAll ( '>' , '>' ) . replaceAll ( '"' , ' & quot; ' ) . replaceAll ( "'" , ''' )
return n
} )
let height = window . innerHeight - 64
addEventListener ( "resize" , e = > {
const new_height = window . innerHeight - 64
height = new_height
container . classList . remove ( "trans" )
table . classList . remove ( "trans" )
locate ( )
setTimeout ( ( ) = > { // give delay to redraw, annoying but needed
container . classList . add ( "trans" )
table . classList . add ( "trans" )
} , 0 )
} )
const container = document . createElement ( "div" )
container . classList . add ( "container" )
container . classList . add ( "trans" )
document . body . appendChild ( container )
const tooltip = document . getElementById ( "tooltip" )
let y = 0
let zoom = 1.0
const table = document . createElement ( "table" )
table . classList . add ( "trans" )
container . appendChild ( table )
const rows = [ ]
function node_t ( idx , depth , offset ) {
if ( ! rows [ depth ] ) {
rows [ depth ] = { el : document.createElement ( "tr" ) , offset : 0 }
}
if ( rows [ depth ] . offset < offset ) {
const pad = document . createElement ( "td" )
pad . colSpan = offset - rows [ depth ] . offset
rows [ depth ] . el . appendChild ( pad )
rows [ depth ] . offset += offset - rows [ depth ] . offset
}
const td = document . createElement ( "td" )
td . innerHTML = '<p><span>' + data . nodes [ idx ] . itemName + ':</span> <b>' + data . nodes [ idx ] . typename + '</b></p>'
td . colSpan = data . nodes [ idx ] . size
td . addEventListener ( "mouseover" , e = > {
const node = data . nodes [ idx ]
tooltip . innerHTML = node . itemName + ": <b>" + node . typename + "</b><br/>"
+ "<ul>"
+ "<li>size = " + node . size + "</li>"
+ "<li>align = " + node . alignment + "</li>"
+ "<li>field offset = " + node . offset + "</li>"
+ "</ul>"
+ "<i>double click to focus</i>"
tooltip . style . display = "block"
} )
td . addEventListener ( "mouseleave" , _ = > tooltip . style . display = "none" )
const total_offset = rows [ depth ] . offset
td . addEventListener ( "dblclick" , e = > {
const node = data . nodes [ idx ]
zoom = data . nodes [ 0 ] . size / node . size
y = - ( total_offset ) / data . nodes [ 0 ] . size * zoom
x = 0
locate ( )
} )
rows [ depth ] . el . appendChild ( td )
rows [ depth ] . offset += data . nodes [ idx ] . size
if ( data . nodes [ idx ] . childrenStart != - 1 ) {
for ( let i = 0 ; i < data . nodes [ idx ] . childrenLen ; i ++ ) {
if ( data . nodes [ data . nodes [ idx ] . childrenStart + i ] . size ) {
node_t ( data . nodes [ idx ] . childrenStart + i , depth + 1 , offset + data . nodes [ data . nodes [ idx ] . childrenStart + i ] . offset )
}
}
}
}
node_t ( 0 , 0 , 0 )
for ( const row of rows ) table . appendChild ( row . el )
const grid = document . createElement ( "div" )
grid . classList . add ( "grid" )
container . appendChild ( grid )
for ( let i = 0 ; i < data . nodes [ 0 ] . size / 8 + 1 ; i ++ ) {
const el = document . createElement ( "div" )
el . classList . add ( "grid-line" )
el . style . top = ( i / ( data . nodes [ 0 ] . size / 8 ) * 100 ) + "%"
el . innerText = i * 8
grid . appendChild ( el )
}
addEventListener ( "mousemove" , e = > {
tooltip . style . top = e . clientY + 10 + "px"
tooltip . style . left = e . clientX + 10 + "px"
} )
function locate() {
container . style . top = height * y + "px"
container . style . height = ( height * zoom ) + "px"
table . style . width = container . style . height
}
locate ( )
} ) ( )
< / script >
2023-06-19 04:24:42 +00:00
< / html > ` ;
2023-06-19 00:31:46 +00:00
ctx . pushExtCleanup ( document ) ;
} ;
}
2023-08-12 06:25:51 +00:00
export function toggleCheckOnSave ( ctx : Ctx ) : Cmd {
return async ( ) = > {
await ctx . config . toggleCheckOnSave ( ) ;
ctx . refreshServerStatus ( ) ;
} ;
}