detectIndentUnit scans leading whitespace across the given lines and returns the smallest consistent indentation unit (one tab, or N spaces where N is the GCD of observed non-zero lead lengths). Returns ("", false) when no useful unit can be detected: no lines have indent, indents mix tabs and space
(lines []string)
| 777 | // Tabs take priority: any tab-indented line forces unit="\t" and any |
| 778 | // space-only indent on another line marks the sample as mixed. |
| 779 | func detectIndentUnit(lines []string) (string, bool) { |
| 780 | sawTab := false |
| 781 | sawSpace := false |
| 782 | var spaceGCD int |
| 783 | for _, l := range lines { |
| 784 | lead, mid, _, _ := splitLineParts(l) |
| 785 | // Skip body-less lines: a blank line or a line with only |
| 786 | // trailing whitespace has no indent signal. Otherwise a |
| 787 | // 2sp whitespace-only line on a 4sp file would corrupt |
| 788 | // the GCD down to 2sp and emit the wrong unit. |
| 789 | if lead == "" || mid == "" { |
| 790 | continue |
| 791 | } |
| 792 | switch { |
| 793 | case strings.HasPrefix(lead, "\t") && !strings.ContainsAny(lead, " "): |
| 794 | sawTab = true |
| 795 | case !strings.ContainsAny(lead, "\t"): |
| 796 | sawSpace = true |
| 797 | if spaceGCD == 0 { |
| 798 | spaceGCD = len(lead) |
| 799 | } else { |
| 800 | spaceGCD = indentGCD(spaceGCD, len(lead)) |
| 801 | } |
| 802 | default: |
| 803 | // Mixed tab+space in a single lead; bail. |
| 804 | return "", false |
| 805 | } |
| 806 | } |
| 807 | if sawTab && sawSpace { |
| 808 | return "", false |
| 809 | } |
| 810 | if sawTab { |
| 811 | return "\t", true |
| 812 | } |
| 813 | if spaceGCD > 0 { |
| 814 | return strings.Repeat(" ", spaceGCD), true |
| 815 | } |
| 816 | return "", false |
| 817 | } |
| 818 | |
| 819 | // indentGCD returns the greatest common divisor of a and b. Used |
| 820 | // only by detectIndentUnit on positive space-lead lengths. |