@@ -29,45 +29,50 @@ import {
2929} from "vscode" ;
3030import { VirtualDoc , VirtualDocUri } from "./vdoc" ;
3131
32+ /**
33+ * Create an on disk temporary file containing the contents of the virtual document
34+ *
35+ * @param virtualDoc The document to use when populating the temporary file
36+ * @param docPath The path to the original document the virtual document is
37+ * based on. When `local` is `true`, this is used to determine the directory
38+ * to create the temporary file in.
39+ * @param local Whether or not the temporary file should be created "locally" in
40+ * the workspace next to `docPath` or in a temporary directory outside the
41+ * workspace.
42+ * @returns A `VirtualDocUri`
43+ */
3244export async function virtualDocUriFromTempFile (
3345 virtualDoc : VirtualDoc ,
3446 docPath : string ,
3547 local : boolean
3648) : Promise < VirtualDocUri > {
37- const newVirtualDocUri = ( doc : TextDocument ) =>
38- < VirtualDocUri > {
39- uri : doc . uri ,
40- cleanup : async ( ) => await deleteDocument ( doc ) ,
41- } ;
42-
43- // if this is local then create it alongside the docPath and return a cleanup
44- // function to remove it when the action is completed.
45- if ( local || virtualDoc . language . localTempFile ) {
46- const ext = virtualDoc . language . extension ;
47- const vdocPath = path . join ( path . dirname ( docPath ) , `.vdoc.${ ext } ` ) ;
48- fs . writeFileSync ( vdocPath , virtualDoc . content ) ;
49- const vdocUri = Uri . file ( vdocPath ) ;
50- const doc = await workspace . openTextDocument ( vdocUri ) ;
51- return newVirtualDocUri ( doc ) ;
52- }
49+ const useLocal = local || virtualDoc . language . localTempFile ;
5350
54- // write the virtual doc as a temp file
55- const vdocTempFile = createVirtualDocTempFile ( virtualDoc ) ;
51+ // If `useLocal`, then create the temporary document alongside the `docPath`
52+ // so tools like formatters have access to workspace configuration. Otherwise,
53+ // create it in a temp directory.
54+ const virtualDocFilepath = useLocal
55+ ? createVirtualDocLocalFile ( virtualDoc , path . dirname ( docPath ) )
56+ : createVirtualDocTempfile ( virtualDoc ) ;
5657
57- // open the document and save a reference to it
58- const vdocUri = Uri . file ( vdocTempFile ) ;
59- const doc = await workspace . openTextDocument ( vdocUri ) ;
58+ const virtualDocUri = Uri . file ( virtualDocFilepath ) ;
59+ const virtualDocTextDocument = await workspace . openTextDocument ( virtualDocUri ) ;
6060
61- // TODO: Reevaluate whether this is necessary. Old comment:
62- // > if this is the first time getting a virtual doc for this
63- // > language then execute a dummy request to cause it to load
64- await commands . executeCommand < Hover [ ] > (
65- "vscode.executeHoverProvider" ,
66- vdocUri ,
67- new Position ( 0 , 0 )
68- ) ;
61+ if ( ! useLocal ) {
62+ // TODO: Reevaluate whether this is necessary. Old comment:
63+ // > if this is the first time getting a virtual doc for this
64+ // > language then execute a dummy request to cause it to load
65+ await commands . executeCommand < Hover [ ] > (
66+ "vscode.executeHoverProvider" ,
67+ virtualDocUri ,
68+ new Position ( 0 , 0 )
69+ ) ;
70+ }
6971
70- return newVirtualDocUri ( doc ) ;
72+ return < VirtualDocUri > {
73+ uri : virtualDocTextDocument . uri ,
74+ cleanup : async ( ) => await deleteDocument ( virtualDocTextDocument ) ,
75+ } ;
7176}
7277
7378// delete a document
@@ -82,19 +87,57 @@ async function deleteDocument(doc: TextDocument) {
8287 }
8388}
8489
85- // create temp files for vdocs. use a base directory that has a subdirectory
86- // for each extension used within the document. this is a no-op if the
87- // file already exists
8890tmp . setGracefulCleanup ( ) ;
89- const vdocTempDir = tmp . dirSync ( ) . name ;
90- function createVirtualDocTempFile ( virtualDoc : VirtualDoc ) {
91- const ext = virtualDoc . language . extension ;
92- const dir = path . join ( vdocTempDir , ext ) ;
93- if ( ! fs . existsSync ( dir ) ) {
94- fs . mkdirSync ( dir ) ;
91+ const VIRTUAL_DOC_TEMP_DIRECTORY = tmp . dirSync ( ) . name ;
92+
93+ /**
94+ * Creates a virtual document in a temporary directory
95+ *
96+ * The temporary directory is automatically cleaned up on process exit.
97+ *
98+ * @param virtualDoc The document to use when populating the temporary file
99+ * @returns The path to the temporary file
100+ */
101+ function createVirtualDocTempfile ( virtualDoc : VirtualDoc ) : string {
102+ const filepath = generateVirtualDocFilepath ( VIRTUAL_DOC_TEMP_DIRECTORY , virtualDoc . language . extension ) ;
103+ createVirtualDoc ( filepath , virtualDoc . content ) ;
104+ return filepath ;
105+ }
106+
107+ /**
108+ * Creates a virtual document in the provided directory
109+ *
110+ * @param virtualDoc The document to use when populating the temporary file
111+ * @param directory The directory to create the temporary file in
112+ * @returns The path to the temporary file
113+ */
114+ function createVirtualDocLocalFile ( virtualDoc : VirtualDoc , directory : string ) : string {
115+ const filepath = generateVirtualDocFilepath ( directory , virtualDoc . language . extension ) ;
116+ createVirtualDoc ( filepath , virtualDoc . content ) ;
117+ return filepath ;
118+ }
119+
120+ /**
121+ * Creates a file filled with the provided content
122+ */
123+ function createVirtualDoc ( filepath : string , content : string ) : void {
124+ const directory = path . dirname ( filepath ) ;
125+
126+ if ( ! fs . existsSync ( directory ) ) {
127+ fs . mkdirSync ( directory ) ;
95128 }
96- const tmpPath = path . join ( vdocTempDir , ext , ".intellisense." + uuid . v4 ( ) + "." + ext ) ;
97- fs . writeFileSync ( tmpPath , virtualDoc . content ) ;
98129
99- return tmpPath ;
130+ fs . writeFileSync ( filepath , content ) ;
131+ }
132+
133+ /**
134+ * Generates a unique virtual document file path
135+ *
136+ * It is important for virtual documents to have unique file paths. If a static
137+ * name like `.vdoc.{ext}` is used, it is possible for one language server
138+ * request to overwrite the contents of the virtual document while another
139+ * language server request is running (#683).
140+ */
141+ function generateVirtualDocFilepath ( directory : string , extension : string ) : string {
142+ return path . join ( directory , ".vdoc." + uuid . v4 ( ) + "." + extension ) ;
100143}
0 commit comments