@@ -66,6 +66,24 @@ export class PowerShellNotebooksFeature extends LanguageClientConsumer {
6666 }
6767}
6868
69+ interface IPowerShellNotebookCellMetadata {
70+ commentType : CommentType ;
71+ openBlockCommentOnOwnLine ?: boolean ;
72+ closeBlockCommentOnOwnLine ?: boolean ;
73+ }
74+
75+ function CreateCell ( cellKind : vscode . CellKind , source : string [ ] , metadata : IPowerShellNotebookCellMetadata ) : vscode . NotebookCellData {
76+ return {
77+ cellKind,
78+ language : cellKind === vscode . CellKind . Markdown ? "markdown" : "powershell" ,
79+ outputs : [ ] ,
80+ source : source . join ( "\n" ) ,
81+ metadata : {
82+ custom : metadata ,
83+ } ,
84+ } ;
85+ }
86+
6987class PowerShellNotebookContentProvider implements vscode . NotebookContentProvider {
7088 private _onDidChangeNotebook = new vscode . EventEmitter < vscode . NotebookDocumentEditEvent > ( ) ;
7189 public onDidChangeNotebook : vscode . Event < vscode . NotebookDocumentEditEvent > = this . _onDidChangeNotebook . event ;
@@ -79,40 +97,72 @@ class PowerShellNotebookContentProvider implements vscode.NotebookContentProvide
7997 this . logger . writeDiagnostic ( `Opening Notebook: ${ uri . toString ( ) } ` ) ;
8098
8199 const data = ( await vscode . workspace . fs . readFile ( actualUri ) ) . toString ( ) ;
82- const lines = data . split ( / \r \n | \r | \n / g) ;
100+
101+ let lines : string [ ] ;
102+ // store the line ending in the metadata of the document
103+ // so that we honor the line ending of the original file
104+ // on save.
105+ let lineEnding : string ;
106+ if ( data . indexOf ( '\r\n' ) !== - 1 ) {
107+ lines = data . split ( / \r \n / g) ;
108+ lineEnding = '\r\n' ;
109+ } else {
110+ lines = data . split ( / \n / g) ;
111+ lineEnding = '\n' ;
112+ }
83113
84114 const notebookData : vscode . NotebookData = {
85115 languages : [ "powershell" ] ,
86116 cells : [ ] ,
87- metadata : { }
117+ metadata : {
118+ custom : {
119+ lineEnding,
120+ }
121+ }
88122 } ;
89123
90124 let currentCellSource : string [ ] = [ ] ;
91125 let cellKind : vscode . CellKind | undefined ;
92126 let insideBlockComment : boolean = false ;
93127
128+ // This dictates whether the BlockComment cell was read in with content on the same
129+ // line as the opening <#. This is so we can preserve the format of the backing file on save.
130+ let openBlockCommentOnOwnLine : boolean = false ;
131+
94132 // Iterate through all lines in a document (aka ps1 file) and group the lines
95133 // into cells (markdown or code) that will be rendered in Notebook mode.
96134 // tslint:disable-next-line: prefer-for-of
97135 for ( let i = 0 ; i < lines . length ; i ++ ) {
98136 // Handle block comments
99137 if ( insideBlockComment ) {
100- if ( lines [ i ] === "#>" ) {
138+ if ( lines [ i ] . endsWith ( "#>" ) ) {
139+ // Get the content of the current line without #>
140+ const currentLine = lines [ i ]
141+ . substring ( 0 , lines [ i ] . length - 2 )
142+ . trimRight ( ) ;
143+
144+ // This dictates whether the BlockComment cell was read in with content on the same
145+ // line as the closing #>. This is so we can preserve the format of the backing file
146+ // on save.
147+ let closeBlockCommentOnOwnLine : boolean = true ;
148+ if ( currentLine ) {
149+ closeBlockCommentOnOwnLine = false ;
150+ currentCellSource . push ( currentLine ) ;
151+ }
152+
101153 // We've reached the end of a block comment,
102154 // push a markdown cell.
103155 insideBlockComment = false ;
104156
105- notebookData . cells . push ( {
106- cellKind : vscode . CellKind . Markdown ,
107- language : "markdown" ,
108- outputs : [ ] ,
109- source : currentCellSource . join ( "\n" ) ,
110- metadata : {
111- custom : {
112- commentType : CommentType . BlockComment
113- }
157+ notebookData . cells . push ( CreateCell (
158+ vscode . CellKind . Markdown ,
159+ currentCellSource ,
160+ {
161+ commentType : CommentType . BlockComment ,
162+ openBlockCommentOnOwnLine,
163+ closeBlockCommentOnOwnLine
114164 }
115- } ) ;
165+ ) ) ;
116166
117167 currentCellSource = [ ] ;
118168 cellKind = null ;
@@ -122,29 +172,65 @@ class PowerShellNotebookContentProvider implements vscode.NotebookContentProvide
122172 // If we're still in a block comment, push the line and continue.
123173 currentCellSource . push ( lines [ i ] ) ;
124174 continue ;
125- } else if ( lines [ i ] === "<#" ) {
175+ } else if ( lines [ i ] . startsWith ( "<#" ) ) {
126176 // If we found the start of a block comment,
127177 // insert what we saw leading up to this.
128178 // If cellKind is null/undefined, that means we
129179 // are starting the file with a BlockComment.
130180 if ( cellKind ) {
131- notebookData . cells . push ( {
181+ notebookData . cells . push ( CreateCell (
132182 cellKind ,
133- language : cellKind === vscode . CellKind . Markdown ? "markdown" : "powershell" ,
134- outputs : [ ] ,
135- source : currentCellSource . join ( "\n" ) ,
136- metadata : {
137- custom : {
138- commentType : cellKind === vscode . CellKind . Markdown ? CommentType . LineComment : CommentType . Disabled ,
139- }
183+ currentCellSource ,
184+ {
185+ commentType : cellKind === vscode . CellKind . Markdown ? CommentType . LineComment : CommentType . Disabled ,
140186 }
141- } ) ;
187+ ) ) ;
142188 }
143189
144- // reset state because we're starting a new Markdown cell.
145- currentCellSource = [ ] ;
190+ // We're starting a new Markdown cell.
146191 cellKind = vscode . CellKind . Markdown ;
147192 insideBlockComment = true ;
193+
194+ // Get the content of the current line without `<#`
195+ const currentLine = lines [ i ]
196+ . substring ( 2 , lines [ i ] . length )
197+ . trimLeft ( ) ;
198+
199+ // If we have additional text on the line with the `<#`
200+ // We need to keep track of what comes after it.
201+ if ( currentLine ) {
202+ // If both the `<#` and the `#>` are on the same line
203+ // we want to push a markdown cell.
204+ if ( currentLine . endsWith ( "#>" ) ) {
205+ // Get the content of the current line without `#>`
206+ const newCurrentLine = currentLine
207+ . substring ( 0 , currentLine . length - 2 )
208+ . trimRight ( ) ;
209+
210+ notebookData . cells . push ( CreateCell (
211+ vscode . CellKind . Markdown ,
212+ [ newCurrentLine ] ,
213+ {
214+ commentType : CommentType . BlockComment ,
215+ openBlockCommentOnOwnLine : false ,
216+ closeBlockCommentOnOwnLine : false ,
217+ }
218+ ) ) ;
219+
220+ // Reset
221+ currentCellSource = [ ] ;
222+ cellKind = null ;
223+ insideBlockComment = false ;
224+ continue ;
225+ }
226+
227+ openBlockCommentOnOwnLine = false ;
228+ currentCellSource = [ currentLine ] ;
229+ } else {
230+ openBlockCommentOnOwnLine = true ;
231+ currentCellSource = [ ] ;
232+ }
233+
148234 continue ;
149235 }
150236
@@ -158,17 +244,13 @@ class PowerShellNotebookContentProvider implements vscode.NotebookContentProvide
158244 } else {
159245 // If cellKind has a value, then we can add the cell we've just computed.
160246 if ( cellKind ) {
161- notebookData . cells . push ( {
162- cellKind : cellKind ! ,
163- language : cellKind === vscode . CellKind . Markdown ? "markdown" : "powershell" ,
164- outputs : [ ] ,
165- source : currentCellSource . join ( "\n" ) ,
166- metadata : {
167- custom : {
168- commentType : cellKind === vscode . CellKind . Markdown ? CommentType . LineComment : CommentType . Disabled ,
169- }
247+ notebookData . cells . push ( CreateCell (
248+ cellKind ,
249+ currentCellSource ,
250+ {
251+ commentType : cellKind === vscode . CellKind . Markdown ? CommentType . LineComment : CommentType . Disabled ,
170252 }
171- } ) ;
253+ ) ) ;
172254 }
173255
174256 // set initial new cell state
@@ -182,17 +264,13 @@ class PowerShellNotebookContentProvider implements vscode.NotebookContentProvide
182264 // when there is only the _start_ of a block comment but not an _end_.)
183265 // add the appropriate cell.
184266 if ( currentCellSource . length ) {
185- notebookData . cells . push ( {
186- cellKind : cellKind ! ,
187- language : cellKind === vscode . CellKind . Markdown ? "markdown" : "powershell" ,
188- outputs : [ ] ,
189- source : currentCellSource . join ( "\n" ) ,
190- metadata : {
191- custom : {
192- commentType : cellKind === vscode . CellKind . Markdown ? CommentType . LineComment : CommentType . Disabled ,
193- }
267+ notebookData . cells . push ( CreateCell (
268+ cellKind ! ,
269+ currentCellSource ,
270+ {
271+ commentType : cellKind === vscode . CellKind . Markdown ? CommentType . LineComment : CommentType . Disabled ,
194272 }
195- } ) ;
273+ ) ) ;
196274 }
197275
198276 return notebookData ;
@@ -228,23 +306,37 @@ class PowerShellNotebookContentProvider implements vscode.NotebookContentProvide
228306 const retArr : string [ ] = [ ] ;
229307 for ( const cell of document . cells ) {
230308 if ( cell . cellKind === vscode . CellKind . Code ) {
231- retArr . push ( ...cell . document . getText ( ) . split ( / \r | \n | \r \n / ) ) ;
309+ retArr . push ( ...cell . document . getText ( ) . split ( / \r \n | \n / ) ) ;
232310 } else {
233311 // First honor the comment type of the cell if it already has one.
234312 // If not, use the user setting.
235313 const commentKind = cell . metadata . custom ?. commentType || Settings . load ( ) . notebooks . saveMarkdownCellsAs ;
236314
237315 if ( commentKind === CommentType . BlockComment ) {
238- retArr . push ( "<#" ) ;
239- retArr . push ( ...cell . document . getText ( ) . split ( / \r | \n | \r \n / ) ) ;
240- retArr . push ( "#>" ) ;
316+ const openBlockCommentOnOwnLine : boolean = cell . metadata . custom ?. openBlockCommentOnOwnLine ;
317+ const closeBlockCommentOnOwnLine : boolean = cell . metadata . custom ?. closeBlockCommentOnOwnLine ;
318+ const text = cell . document . getText ( ) . split ( / \r \n | \n / ) ;
319+ if ( openBlockCommentOnOwnLine ) {
320+ retArr . push ( "<#" ) ;
321+ } else {
322+ text [ 0 ] = `<# ${ text [ 0 ] } ` ;
323+ }
324+
325+ if ( ! closeBlockCommentOnOwnLine ) {
326+ text [ text . length - 1 ] += " #>" ;
327+ retArr . push ( ...text ) ;
328+ } else {
329+ retArr . push ( ...text ) ;
330+ retArr . push ( "#>" ) ;
331+ }
241332 } else {
242- retArr . push ( ...cell . document . getText ( ) . split ( / \r | \n | \r \n / ) . map ( ( line ) => `# ${ line } ` ) ) ;
333+ retArr . push ( ...cell . document . getText ( ) . split ( / \r \n | \n / ) . map ( ( line ) => `# ${ line } ` ) ) ;
243334 }
244335 }
245336 }
246337
247- await vscode . workspace . fs . writeFile ( targetResource , new TextEncoder ( ) . encode ( retArr . join ( "\n" ) ) ) ;
338+ const eol = document . metadata . custom . lineEnding ;
339+ await vscode . workspace . fs . writeFile ( targetResource , new TextEncoder ( ) . encode ( retArr . join ( eol ) ) ) ;
248340 }
249341}
250342
0 commit comments