55import com .intellij .openapi .components .ServiceManager ;
66import com .intellij .openapi .components .State ;
77import com .intellij .openapi .components .Storage ;
8+ import com .intellij .openapi .editor .Document ;
9+ import com .intellij .openapi .fileEditor .FileDocumentManager ;
810import com .intellij .openapi .fileTypes .LanguageFileType ;
911import com .intellij .openapi .project .Project ;
1012import com .intellij .openapi .vfs .VirtualFile ;
1113import com .intellij .psi .PsiFile ;
1214import com .intellij .util .xmlb .XmlSerializerUtil ;
1315import com .intellij .util .xmlb .annotations .OptionTag ;
16+ import net .seesharpsoft .commons .collection .Pair ;
1417import net .seesharpsoft .intellij .plugins .csv .*;
1518import net .seesharpsoft .intellij .plugins .csv .settings .CsvEditorSettings ;
19+ import org .apache .commons .lang .StringUtils ;
1620import org .jetbrains .annotations .NotNull ;
1721import org .jetbrains .annotations .Nullable ;
1822
23+ import java .util .Arrays ;
1924import java .util .HashMap ;
2025import java .util .Map ;
2126
@@ -87,12 +92,18 @@ public boolean canChangeValueSeparator(@NotNull PsiFile psiFile) {
8792 return language .isKindOf (CsvLanguage .INSTANCE ) && !(language instanceof CsvSeparatorHolder );
8893 }
8994
95+ private void setFileSeparator (@ NotNull Project project , @ NotNull VirtualFile virtualFile , @ NotNull CsvValueSeparator separator ) {
96+ Attribute attribute = getFileAttribute (project , virtualFile , true );
97+ if (attribute != null ) {
98+ attribute .separator = separator ;
99+ }
100+ }
101+
90102 public void setFileSeparator (@ NotNull PsiFile psiFile , @ NotNull CsvValueSeparator separator ) {
91103 if (!canChangeValueSeparator (psiFile )) {
92104 return ;
93105 }
94- Attribute attribute = getFileAttribute (psiFile .getProject (), psiFile .getOriginalFile ().getVirtualFile (), true );
95- attribute .separator = separator ;
106+ setFileSeparator (psiFile .getProject (), psiFile .getOriginalFile ().getVirtualFile (), separator );
96107 }
97108
98109 public void resetValueSeparator (@ NotNull PsiFile psiFile ) {
@@ -105,18 +116,51 @@ public void resetValueSeparator(@NotNull PsiFile psiFile) {
105116 }
106117 }
107118
119+ private @ NotNull
120+ CsvValueSeparator guessOrGetValueSeparator (Project project , VirtualFile virtualFile ) {
121+ return CsvEditorSettings .getInstance ().isGuessValueSeparator () ?
122+ guessValueSeparator (project , virtualFile ) :
123+ CsvEditorSettings .getInstance ().getDefaultValueSeparator ();
124+ }
125+
126+ private @ NotNull
127+ CsvValueSeparator guessValueSeparator (Project project , VirtualFile virtualFile ) {
128+ Document document = FileDocumentManager .getInstance ().getDocument (virtualFile );
129+ final String text = document == null ? "" : document .getText ();
130+ Pair <CsvValueSeparator , Integer > separatorWithCount =
131+ Arrays .stream (CsvValueSeparator .values ())
132+ // count
133+ .map (separator -> {
134+ String character = separator .getCharacter ();
135+ return Pair .of (separator , StringUtils .countMatches (text , character ));
136+ })
137+ // ignore non-matched separators
138+ .filter (p -> p .getSecond () > 0 )
139+ // get the one with most hits
140+ .max ((p1 , p2 ) -> p1 .getSecond () - p2 .getSecond ())
141+ // failsafe (e.g. empty document)
142+ .orElse (null );
143+
144+ CsvValueSeparator valueSeparator = separatorWithCount != null ?
145+ separatorWithCount .getFirst () :
146+ CsvEditorSettings .getInstance ().getDefaultValueSeparator ();
147+
148+ setFileSeparator (project , virtualFile , valueSeparator );
149+ return valueSeparator ;
150+ }
151+
108152 public @ NotNull
109153 CsvValueSeparator getValueSeparator (Project project , VirtualFile virtualFile ) {
110154 if (project == null || virtualFile == null || !(virtualFile .getFileType () instanceof LanguageFileType )) {
111155 return CsvEditorSettings .getInstance ().getDefaultValueSeparator ();
112156 }
113157 Language language = ((LanguageFileType ) virtualFile .getFileType ()).getLanguage ();
114158 if (language instanceof CsvSeparatorHolder ) {
115- return ((CsvSeparatorHolder ) language ).getSeparator ();
159+ return ((CsvSeparatorHolder ) language ).getSeparator ();
116160 }
117161 Attribute attribute = getFileAttribute (project , virtualFile );
118162 return attribute == null || attribute .separator == null ?
119- CsvEditorSettings . getInstance (). getDefaultValueSeparator ( ) :
163+ guessOrGetValueSeparator ( project , virtualFile ) :
120164 attribute .separator ;
121165 }
122166
@@ -127,7 +171,9 @@ public boolean hasValueSeparatorAttribute(@NotNull Project project, @NotNull Vir
127171
128172 public void setEscapeCharacter (@ NotNull PsiFile psiFile , @ NotNull CsvEscapeCharacter escapeCharacter ) {
129173 Attribute attribute = getFileAttribute (psiFile .getProject (), psiFile .getOriginalFile ().getVirtualFile (), true );
130- attribute .escapeCharacter = escapeCharacter ;
174+ if (attribute != null ) {
175+ attribute .escapeCharacter = escapeCharacter ;
176+ }
131177 }
132178
133179 public void resetEscapeSeparator (@ NotNull PsiFile psiFile ) {
0 commit comments