@@ -29,45 +29,50 @@ import {
29
29
} from "vscode" ;
30
30
import { VirtualDoc , VirtualDocUri } from "./vdoc" ;
31
31
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
+ */
32
44
export async function virtualDocUriFromTempFile (
33
45
virtualDoc : VirtualDoc ,
34
46
docPath : string ,
35
47
local : boolean
36
48
) : 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 ;
53
50
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 ) ;
56
57
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 ) ;
60
60
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
+ }
69
71
70
- return newVirtualDocUri ( doc ) ;
72
+ return < VirtualDocUri > {
73
+ uri : virtualDocTextDocument . uri ,
74
+ cleanup : async ( ) => await deleteDocument ( virtualDocTextDocument ) ,
75
+ } ;
71
76
}
72
77
73
78
// delete a document
@@ -82,19 +87,57 @@ async function deleteDocument(doc: TextDocument) {
82
87
}
83
88
}
84
89
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
88
90
tmp . 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 ) ;
95
128
}
96
- const tmpPath = path . join ( vdocTempDir , ext , ".intellisense." + uuid . v4 ( ) + "." + ext ) ;
97
- fs . writeFileSync ( tmpPath , virtualDoc . content ) ;
98
129
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 ) ;
100
143
}
0 commit comments