@@ -728,9 +728,9 @@ protected override void ApplyDocumentTextChanged (DocumentId id, SourceText text
728
728
tryApplyState_documentTextChangedTasks . Add ( ApplyDocumentTextChangedCore ( id , text ) ) ;
729
729
}
730
730
731
- async Task ApplyDocumentTextChangedCore ( DocumentId id , SourceText text )
731
+ async Task ApplyDocumentTextChangedCore ( DocumentId id , SourceText text , bool isAnalyzerConfigFile = false )
732
732
{
733
- var document = GetDocument ( id ) ;
733
+ TextDocument document = isAnalyzerConfigFile ? GetAnalyzerConfigDocument ( id ) : GetDocument ( id ) ;
734
734
if ( document == null )
735
735
return ;
736
736
@@ -747,12 +747,21 @@ async Task ApplyDocumentTextChangedCore (DocumentId id, SourceText text)
747
747
return ;
748
748
}
749
749
}
750
- var ( projection , filePath ) = Projections . Get ( document . FilePath ) ;
751
- var data = TextFileProvider . Instance . GetTextEditorData ( filePath , out bool isOpen ) ;
752
- // Guard against already done changes in linked files.
753
- // This shouldn't happen but the roslyn merging seems not to be working correctly in all cases :/
754
- if ( document . GetLinkedDocumentIds ( ) . Length > 0 && isOpen && ! ( text . GetType ( ) . FullName == "Microsoft.CodeAnalysis.Text.ChangedText" ) ) {
755
- return ;
750
+ Projection projection = null ;
751
+ FilePath filePath ;
752
+ ITextDocument data = null ;
753
+ bool isOpen ;
754
+ if ( isAnalyzerConfigFile ) {
755
+ filePath = document . FilePath ;
756
+ data = TextFileProvider . Instance . GetTextEditorData ( filePath , out isOpen ) ;
757
+ } else {
758
+ ( projection , filePath ) = Projections . Get ( document . FilePath ) ;
759
+ data = TextFileProvider . Instance . GetTextEditorData ( filePath , out isOpen ) ;
760
+ // Guard against already done changes in linked files.
761
+ // This shouldn't happen but the roslyn merging seems not to be working correctly in all cases :/
762
+ if ( ( ( Document ) document ) . GetLinkedDocumentIds ( ) . Length > 0 && isOpen && ! ( text . GetType ( ) . FullName == "Microsoft.CodeAnalysis.Text.ChangedText" ) ) {
763
+ return ;
764
+ }
756
765
}
757
766
758
767
lock ( tryApplyState_documentTextChangedContents ) {
@@ -796,20 +805,29 @@ async Task ApplyDocumentTextChangedCore (DocumentId id, SourceText text)
796
805
}
797
806
data . Save ( ) ;
798
807
if ( projection != null ) {
799
- await UpdateProjectionsDocuments ( document , data ) ;
808
+ await UpdateProjectionsDocuments ( ( Document ) document , data ) ;
809
+ } else if ( isAnalyzerConfigFile ) {
810
+ OnAnalyzerConfigDocumentTextChanged ( id , new MonoDevelopSourceText ( data ) , PreservationMode . PreserveValue ) ;
800
811
} else {
801
812
OnDocumentTextChanged ( id , new MonoDevelopSourceText ( data ) , PreservationMode . PreserveValue ) ;
802
813
}
803
814
} else {
804
815
var formatter = CodeFormatterService . GetFormatter ( data . MimeType ) ;
805
816
var documentContext = documentManager . Documents . FirstOrDefault ( d => FilePath . PathComparer . Compare ( d . FileName , filePath ) == 0 ) ? . DocumentContext ;
806
- var root = await projectChanges . NewProject . GetDocument ( id ) . GetSyntaxRootAsync ( ) ;
807
- var annotatedNode = root . DescendantNodesAndSelf ( ) . FirstOrDefault ( n => n . HasAnnotation ( typeSystemService . InsertionModeAnnotation ) ) ;
808
- SyntaxToken ? renameTokenOpt = root . GetAnnotatedNodesAndTokens ( Microsoft . CodeAnalysis . CodeActions . RenameAnnotation . Kind )
809
- . Where ( s => s . IsToken )
810
- . Select ( s => s . AsToken ( ) )
811
- . Cast < SyntaxToken ? > ( )
812
- . FirstOrDefault ( ) ;
817
+
818
+ SyntaxNode root = null ;
819
+ SyntaxNode annotatedNode = null ;
820
+ SyntaxToken ? renameTokenOpt = null ;
821
+
822
+ if ( ! isAnalyzerConfigFile ) {
823
+ root = await projectChanges . NewProject . GetDocument ( id ) . GetSyntaxRootAsync ( ) ;
824
+ annotatedNode = root . DescendantNodesAndSelf ( ) . FirstOrDefault ( n => n . HasAnnotation ( typeSystemService . InsertionModeAnnotation ) ) ;
825
+ renameTokenOpt = root . GetAnnotatedNodesAndTokens ( Microsoft . CodeAnalysis . CodeActions . RenameAnnotation . Kind )
826
+ . Where ( s => s . IsToken )
827
+ . Select ( s => s . AsToken ( ) )
828
+ . Cast < SyntaxToken ? > ( )
829
+ . FirstOrDefault ( ) ;
830
+ }
813
831
814
832
if ( documentContext != null ) {
815
833
var editor = ( TextEditor ) data ;
@@ -933,7 +951,9 @@ await Runtime.RunInMainThread (async () => {
933
951
}
934
952
935
953
if ( projection != null ) {
936
- await UpdateProjectionsDocuments ( document , data ) ;
954
+ await UpdateProjectionsDocuments ( ( Document ) document , data ) ;
955
+ } else if ( isAnalyzerConfigFile ) {
956
+ OnAnalyzerConfigDocumentTextChanged ( id , new MonoDevelopSourceText ( data ) , PreservationMode . PreserveValue ) ;
937
957
} else {
938
958
OnDocumentTextChanged ( id , new MonoDevelopSourceText ( data ) , PreservationMode . PreserveValue ) ;
939
959
}
@@ -943,100 +963,8 @@ await Runtime.RunInMainThread (async () => {
943
963
protected override void ApplyAnalyzerConfigDocumentTextChanged ( DocumentId id , SourceText text )
944
964
{
945
965
lock ( projectModifyLock )
946
- tryApplyState_documentTextChangedTasks . Add ( ApplyAnalyzerConfigDocumentTextChangedCore ( id , text ) ) ;
966
+ tryApplyState_documentTextChangedTasks . Add ( ApplyDocumentTextChangedCore ( id , text , isAnalyzerConfigFile : true ) ) ;
947
967
}
948
-
949
- /// <summary>
950
- /// TODO: Fix code duplication with ApplyDocumentTextChangedCore
951
- /// </summary>
952
- async Task ApplyAnalyzerConfigDocumentTextChangedCore ( DocumentId id , SourceText text )
953
- {
954
- var document = GetAnalyzerConfigDocument ( id ) ;
955
- if ( document == null )
956
- return ;
957
-
958
- var hostDocument = MonoDevelopHostDocumentRegistration . FromDocument ( document ) ;
959
- if ( hostDocument != null ) {
960
- hostDocument . UpdateText ( text ) ;
961
- return ;
962
- }
963
- if ( IsDocumentOpen ( id ) ) {
964
- var textBuffer = ( await document . GetTextAsync ( CancellationToken . None ) ) . Container . TryGetTextBuffer ( ) ;
965
-
966
- if ( textBuffer != null ) {
967
- UpdateText ( text , textBuffer , Microsoft . VisualStudio . Text . EditOptions . DefaultMinimalChange ) ;
968
- return ;
969
- }
970
- }
971
- var filePath = document . FilePath ;
972
- var data = TextFileProvider . Instance . GetTextEditorData ( filePath , out bool isOpen ) ;
973
-
974
- lock ( tryApplyState_documentTextChangedContents ) {
975
- if ( tryApplyState_documentTextChangedContents . TryGetValue ( filePath , out SourceText formerText ) ) {
976
- if ( formerText . Length == text . Length && formerText . ToString ( ) == text . ToString ( ) )
977
- return ;
978
- }
979
- tryApplyState_documentTextChangedContents [ filePath ] = text ;
980
- }
981
-
982
- if ( ! isOpen || ! document . TryGetText ( out SourceText oldFile ) ) {
983
- oldFile = await document . GetTextAsync ( ) ;
984
- }
985
- var changes = text . GetTextChanges ( oldFile ) . OrderByDescending ( c => c . Span . Start ) . ToList ( ) ;
986
- int delta = 0 ;
987
-
988
- if ( ! isOpen ) {
989
- delta = ApplyChanges ( null , data , changes ) ;
990
- var formatter = CodeFormatterService . GetFormatter ( data . MimeType ) ;
991
- if ( formatter != null && formatter . SupportsPartialDocumentFormatting ) {
992
- var mp = GetMonoProject ( CurrentSolution . GetProject ( id . ProjectId ) ) ;
993
- string currentText = data . Text ;
994
-
995
- foreach ( var change in changes ) {
996
- delta -= change . Span . Length - change . NewText . Length ;
997
- var startOffset = change . Span . Start - delta ;
998
-
999
- string str ;
1000
- if ( change . NewText . Length == 0 ) {
1001
- str = formatter . FormatText ( mp . Policies , currentText , TextSegment . FromBounds ( Math . Max ( 0 , startOffset - 1 ) , Math . Min ( data . Length , startOffset + 1 ) ) ) ;
1002
- } else {
1003
- str = formatter . FormatText ( mp . Policies , currentText , new TextSegment ( startOffset , change . NewText . Length ) ) ;
1004
- }
1005
- data . ReplaceText ( startOffset , change . NewText . Length , str ) ;
1006
- }
1007
- }
1008
- data . Save ( ) ;
1009
- OnAnalyzerConfigDocumentTextChanged ( id , new MonoDevelopSourceText ( data ) , PreservationMode . PreserveValue ) ;
1010
- } else {
1011
- var formatter = CodeFormatterService . GetFormatter ( data . MimeType ) ;
1012
- var documentContext = documentManager . Documents . FirstOrDefault ( d => FilePath . PathComparer . Compare ( d . FileName , filePath ) == 0 ) ? . DocumentContext ;
1013
-
1014
- if ( documentContext != null ) {
1015
- var editor = ( TextEditor ) data ;
1016
- await Runtime . RunInMainThread ( async ( ) => {
1017
- using ( var undo = editor . OpenUndoGroup ( ) ) {
1018
- var oldVersion = editor . Version ;
1019
- delta = ApplyChanges ( null , data , changes ) ;
1020
- var versionBeforeFormat = editor . Version ;
1021
-
1022
- if ( formatter != null && formatter . SupportsOnTheFlyFormatting ) {
1023
- foreach ( var change in changes ) {
1024
- delta -= change . Span . Length - change . NewText . Length ;
1025
- var startOffset = change . Span . Start - delta ;
1026
- if ( change . NewText . Length == 0 ) {
1027
- formatter . OnTheFlyFormat ( editor , documentContext , TextSegment . FromBounds ( Math . Max ( 0 , startOffset - 1 ) , Math . Min ( data . Length , startOffset + 1 ) ) ) ;
1028
- } else {
1029
- formatter . OnTheFlyFormat ( editor , documentContext , new TextSegment ( startOffset , change . NewText . Length ) ) ;
1030
- }
1031
- }
1032
- }
1033
- }
1034
- } ) ;
1035
- }
1036
-
1037
- OnAnalyzerConfigDocumentTextChanged ( id , new MonoDevelopSourceText ( data ) , PreservationMode . PreserveValue ) ;
1038
- }
1039
- }
1040
968
1041
969
internal static Func < TextEditor , int , Task < List < InsertionPoint > > > GetInsertionPoints ;
1042
970
internal static Action < TextEditor , DocumentContext , ITextSourceVersion , SyntaxToken ? > StartRenameSession ;
@@ -1184,99 +1112,66 @@ protected override void ApplyProjectChanges (ProjectChanges projectChanges)
1184
1112
1185
1113
protected override void ApplyDocumentAdded ( DocumentInfo info , SourceText text )
1186
1114
{
1187
- var id = info . Id ;
1188
- MonoDevelop . Projects . Project mdProject = null ;
1189
-
1190
- if ( id . ProjectId != null ) {
1191
- var project = CurrentSolution . GetProject ( id . ProjectId ) ;
1192
- mdProject = GetMonoProject ( project ) ;
1193
- if ( mdProject == null )
1194
- LoggingService . LogWarning ( "Couldn't find project for newly generated file {0} (Project {1})." , info . Name , info . Id . ProjectId ) ;
1195
- }
1115
+ var mdProject = GetMonoProject ( info ) ;
1196
1116
1197
- var path = DetermineFilePath ( info . Id , info . Name , info . FilePath , info . Folders , mdProject ? . FileName . ParentDirectory , true ) ;
1117
+ var path = DetermineFilePath ( info , mdProject , true ) ;
1198
1118
// If file is already part of project don't re-add it, example of this is .cshtml
1199
1119
if ( mdProject ? . IsFileInProject ( path ) == true ) {
1200
1120
this . OnDocumentAdded ( info ) ;
1201
1121
return ;
1202
1122
}
1203
1123
info = info . WithFilePath ( path ) . WithTextLoader ( new MonoDevelopTextLoader ( path ) ) ;
1204
1124
1205
- string formattedText ;
1206
- var formatter = CodeFormatterService . GetFormatter ( desktopService . GetMimeTypeForUri ( path ) ) ;
1207
- if ( formatter != null && mdProject != null ) {
1208
- formattedText = formatter . FormatText ( mdProject . Policies , text . ToString ( ) ) ;
1209
- } else {
1210
- formattedText = text . ToString ( ) ;
1211
- }
1212
-
1213
- var textSource = new StringTextSource ( formattedText , text . Encoding ?? System . Text . Encoding . UTF8 ) ;
1214
- try {
1215
- textSource . WriteTextTo ( path ) ;
1216
- } catch ( Exception e ) {
1217
- LoggingService . LogError ( "Exception while saving file to " + path , e ) ;
1218
- }
1125
+ FormatFile ( text , mdProject , path ) ;
1219
1126
1220
1127
if ( mdProject != null ) {
1221
- var data = ProjectMap . GetData ( id . ProjectId ) ;
1128
+ var data = ProjectMap . GetData ( info . Id . ProjectId ) ;
1222
1129
data . DocumentData . Add ( info . Id , path ) ;
1223
1130
var file = new MonoDevelop . Projects . ProjectFile ( path ) ;
1224
1131
mdProject . Files . Add ( file ) ;
1225
1132
tryApplyState_changedProjects . Add ( mdProject ) ;
1226
1133
}
1227
1134
1228
1135
this . OnDocumentAdded ( info ) ;
1229
- }
1230
-
1231
- /// <summary>
1232
- /// TODO: Code is similar to ApplyDocumentAdded. Can we share code similar to VisualStudioWorkspaceImpl?
1233
- /// https://github.com/dotnet/roslyn/blob/3149ba26d3ea5949532028c71b9533658a8cab8b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs#L719-L732
1234
- /// </summary>
1235
- protected override void ApplyAnalyzerConfigDocumentAdded ( DocumentInfo info , SourceText text )
1236
- {
1237
- var id = info . Id ;
1238
- MonoDevelop . Projects . Project mdProject = null ;
1136
+ }
1239
1137
1138
+ MonoDevelop . Projects . Project GetMonoProject ( DocumentInfo info )
1139
+ {
1140
+ var id = info . Id ;
1240
1141
if ( id . ProjectId != null ) {
1241
1142
var project = CurrentSolution . GetProject ( id . ProjectId ) ;
1242
- mdProject = GetMonoProject ( project ) ;
1143
+ var mdProject = GetMonoProject ( project ) ;
1243
1144
if ( mdProject == null )
1244
- LoggingService . LogWarning ( "Couldn't find project for newly generated file {0} (Project {1})." , info . Name , info . Id . ProjectId ) ;
1145
+ LoggingService . LogWarning ( "Couldn't find project for document {0} (Project {1})." , info . Name , id . ProjectId ) ;
1146
+ return mdProject ;
1245
1147
}
1148
+ return null ;
1149
+ }
1150
+
1151
+ protected override void ApplyAnalyzerConfigDocumentAdded ( DocumentInfo info , SourceText text )
1152
+ {
1153
+ var mdProject = GetMonoProject ( info ) ;
1246
1154
1247
- var path = DetermineFilePath ( info . Id , info . Name , info . FilePath , info . Folders , mdProject ? . FileName . ParentDirectory , true ) ;
1248
- // If file is already part of project don't re-add it, example of this is .cshtml
1155
+ var path = DetermineFilePath ( info , mdProject , true ) ;
1156
+ // If file is already part of project don't re-add it unless it does not exist.
1249
1157
if ( mdProject ? . IsFileInProject ( path ) == true && File . Exists ( path ) ) {
1250
1158
OnAnalyzerConfigDocumentAdded ( info ) ;
1251
1159
return ;
1252
1160
}
1253
- info = info . WithFilePath ( path ) . WithTextLoader ( new MonoDevelopTextLoader ( path ) ) ;
1254
-
1255
- string formattedText ;
1256
- var formatter = CodeFormatterService . GetFormatter ( desktopService . GetMimeTypeForUri ( path ) ) ;
1257
- if ( formatter != null && mdProject != null ) {
1258
- formattedText = formatter . FormatText ( mdProject . Policies , text . ToString ( ) ) ;
1259
- } else {
1260
- formattedText = text . ToString ( ) ;
1261
- }
1262
-
1263
- var textSource = new StringTextSource ( formattedText , text . Encoding ?? System . Text . Encoding . UTF8 ) ;
1264
- try {
1265
- textSource . WriteTextTo ( path ) ;
1266
- } catch ( Exception e ) {
1267
- LoggingService . LogError ( "Exception while saving file to " + path , e ) ;
1268
- }
1161
+ info = info . WithFilePath ( path ) . WithTextLoader ( new MonoDevelopTextLoader ( path ) ) ;
1162
+
1163
+ FormatFile ( text , mdProject , path ) ;
1269
1164
1270
1165
if ( mdProject != null ) {
1271
- var data = ProjectMap . GetData ( id . ProjectId ) ;
1166
+ var data = ProjectMap . GetData ( info . Id . ProjectId ) ;
1272
1167
data . DocumentData . Add ( info . Id , path ) ;
1273
1168
1274
1169
var file = new MonoDevelop . Projects . ProjectFile ( path , MonoDevelop . Projects . BuildAction . None ) ;
1275
- if ( ! file . FilePath . IsChildPathOf ( mdProject . BaseDirectory ) ) {
1170
+ if ( ! file . FilePath . IsChildPathOf ( mdProject . BaseDirectory ) ) {
1276
1171
// Outside project directory - add it as a solution folder item.
1277
1172
var solutionFolder = GetSolutionItemsFolder ( mdProject ) ;
1278
1173
if ( ! solutionFolder . Files . Contains ( path ) ) {
1279
- solutionFolder . Files . Add ( path ) ;
1174
+ solutionFolder . Files . Add ( path ) ;
1280
1175
mdProject . ParentSolution . SaveAsync ( new ProgressMonitor ( ) ) . Ignore ( ) ;
1281
1176
}
1282
1177
}
@@ -1294,6 +1189,24 @@ protected override void ApplyAnalyzerConfigDocumentAdded (DocumentInfo info, Sou
1294
1189
OnAnalyzerConfigDocumentAdded ( info ) ;
1295
1190
}
1296
1191
1192
+ void FormatFile ( SourceText text , MonoDevelop . Projects . Project mdProject , string path )
1193
+ {
1194
+ string formattedText ;
1195
+ var formatter = CodeFormatterService . GetFormatter ( desktopService . GetMimeTypeForUri ( path ) ) ;
1196
+ if ( formatter != null && mdProject != null ) {
1197
+ formattedText = formatter . FormatText ( mdProject . Policies , text . ToString ( ) ) ;
1198
+ } else {
1199
+ formattedText = text . ToString ( ) ;
1200
+ }
1201
+
1202
+ var textSource = new StringTextSource ( formattedText , text . Encoding ?? System . Text . Encoding . UTF8 ) ;
1203
+ try {
1204
+ textSource . WriteTextTo ( path ) ;
1205
+ } catch ( Exception e ) {
1206
+ LoggingService . LogError ( "Exception while saving file to " + path , e ) ;
1207
+ }
1208
+ }
1209
+
1297
1210
MonoDevelop . Projects . SolutionFolder GetSolutionItemsFolder ( MonoDevelop . Projects . Project project )
1298
1211
{
1299
1212
string name = GettextCatalog . GetString ( "Solution Items" ) ;
@@ -1343,16 +1256,17 @@ protected override void ApplyAnalyzerConfigDocumentRemoved (DocumentId documentI
1343
1256
base . ApplyAnalyzerConfigDocumentRemoved ( documentId ) ;
1344
1257
}
1345
1258
1346
- string DetermineFilePath ( DocumentId id , string name , string filePath , IReadOnlyList < string > docFolders , string defaultFolder , bool createDirectory = false )
1259
+ string DetermineFilePath ( DocumentInfo info , MonoDevelop . Projects . Project mdProject , bool createDirectory = false )
1347
1260
{
1348
- var path = filePath ;
1261
+ var path = info . FilePath ;
1349
1262
1350
1263
if ( string . IsNullOrEmpty ( path ) ) {
1351
- var monoProject = GetMonoProject ( id . ProjectId ) ;
1264
+ var monoProject = GetMonoProject ( info . Id . ProjectId ) ;
1352
1265
1353
1266
// If the first namespace name matches the name of the project, then we don't want to
1354
1267
// generate a folder for that. The project is implicitly a folder with that name.
1355
1268
IEnumerable < string > folders ;
1269
+ var docFolders = info . Folders ;
1356
1270
if ( docFolders != null && monoProject != null && docFolders . FirstOrDefault ( ) == monoProject . Name ) {
1357
1271
folders = docFolders . Skip ( 1 ) ;
1358
1272
} else {
@@ -1367,9 +1281,9 @@ string DetermineFilePath (DocumentId id, string name, string filePath, IReadOnly
1367
1281
} catch ( Exception e ) {
1368
1282
LoggingService . LogError ( "Error while creating directory for a new file : " + baseDirectory , e ) ;
1369
1283
}
1370
- path = Path . Combine ( baseDirectory , name ) ;
1284
+ path = Path . Combine ( baseDirectory , info . Name ) ;
1371
1285
} else {
1372
- path = Path . Combine ( defaultFolder , name ) ;
1286
+ path = Path . Combine ( mdProject ? . FileName . ParentDirectory , info . Name ) ;
1373
1287
}
1374
1288
}
1375
1289
return path ;
0 commit comments