Skip to content

Commit

Permalink
Spellcheck file name (#1368)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt committed Jan 21, 2024
1 parent 2ed4a08 commit 8633f9e
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 169 deletions.
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Publish NuGet packages that provide [refactorings](https://www.nuget.org/packages/roslynator.refactorings) and [code fixes for compiler diagnostics](https://www.nuget.org/packages/roslynator.codefixes) ([PR](https://github.com/dotnet/roslynator/pull/1358))
- These packages are recommended to be used in an environment where Roslynator IDE extension cannot be used, e.g. VS Code + C# Dev Kit (see related [issue](https://github.com/dotnet/vscode-csharp/issues/6790))
- Add analyzer "Remove redundant catch block" [RCS1265](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1265) ([PR](https://github.com/dotnet/roslynator/pull/1364) by @jakubreznak)
- [CLI] Spellcheck file names ([PR](https://github.com/dotnet/roslynator/pull/1368))
- `roslynator spellcheck --scope file-name`

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context)
_options,
context.CancellationToken);

if ((_options.ScopeFilter & SpellingScopeFilter.FileName) != 0)
analysisContext.AnalyzeFileName(tree);

CSharpSpellingWalker walker = CSharpSpellingWalker.Create(analysisContext);

walker.Visit(root);
Expand Down
9 changes: 3 additions & 6 deletions src/CommandLine/Commands/SpellcheckCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ public override async Task<SpellcheckCommandResult> ExecuteAsync(ProjectOrSoluti
MinWordLength = Options.MinWordLength,
MaxWordLength = Options.MaxWordLength,
IncludeGeneratedCode = Options.IncludeGeneratedCode,
#if DEBUG
Autofix = !Options.NoAutofix,
#endif
Interactive = Options.Interactive,
DryRun = Options.DryRun,
};
Expand All @@ -87,7 +84,7 @@ public override async Task<SpellcheckCommandResult> ExecuteAsync(ProjectOrSoluti

Solution solution = project.Solution;

spellingFixer = GetSpellingFixer(solution);
spellingFixer = GetSpellcheckAnalyzer(solution);

WriteLine($"Analyze '{project.Name}'", ConsoleColors.Cyan, Verbosity.Minimal);

Expand All @@ -103,7 +100,7 @@ public override async Task<SpellcheckCommandResult> ExecuteAsync(ProjectOrSoluti
{
Solution solution = projectOrSolution.AsSolution();

spellingFixer = GetSpellingFixer(solution);
spellingFixer = GetSpellcheckAnalyzer(solution);

results = await spellingFixer.FixSolutionAsync(f => IsMatch(f), cancellationToken);
}
Expand All @@ -118,7 +115,7 @@ public override async Task<SpellcheckCommandResult> ExecuteAsync(ProjectOrSoluti
: CommandStatus.NotSuccess,
results);

SpellcheckAnalyzer GetSpellingFixer(Solution solution)
SpellcheckAnalyzer GetSpellcheckAnalyzer(Solution solution)
{
return new SpellcheckAnalyzer(
solution,
Expand Down
11 changes: 3 additions & 8 deletions src/CommandLine/Options/SpellcheckCommandLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public sealed class SpellcheckCommandLineOptions : MSBuildCommandLineOptions

[Option(
longName: OptionNames.IgnoredScope,
HelpText = "Defines syntax that should not be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol and symbol.",
HelpText = "Defines syntax that should not be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol and file-name.",
MetaValue = "<SCOPE>")]
public IEnumerable<string> IgnoredScope { get; set; }

Expand All @@ -60,15 +60,10 @@ public sealed class SpellcheckCommandLineOptions : MSBuildCommandLineOptions
HelpText = "Specifies minimal word length to be checked. Default value is 3.",
MetaValue = "<NUM>")]
public int MinWordLength { get; set; }
#if DEBUG
[Option(
longName: OptionNames.NoAutofix,
HelpText = "Disable applying predefined fixes.")]
public bool NoAutofix { get; set; }
#endif

[Option(
longName: OptionNames.Scope,
HelpText = "Defines syntax that should be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol and all. Literals are not analyzed by default.",
HelpText = "Defines syntax that should be analyzed. Allowed values are comment, type, member, local, parameter, literal, non-symbol, symbol, file-name and all. Literals and file names are not analyzed by default.",
MetaValue = "<SCOPE>")]
public IEnumerable<string> Scope { get; set; }

Expand Down
14 changes: 13 additions & 1 deletion src/CommandLine/Rename/CliSymbolRenameState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ namespace Roslynator.CommandLine.Rename;

internal class CliSymbolRenameState : SymbolRenameState
{
private static readonly SymbolDisplayFormat _symbolDefinitionFormat = SymbolDisplayFormat.CSharpErrorMessageFormat.Update(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut
| SymbolDisplayParameterOptions.IncludeType
| SymbolDisplayParameterOptions.IncludeName
| SymbolDisplayParameterOptions.IncludeDefaultValue,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
| SymbolDisplayMiscellaneousOptions.UseSpecialTypes
| SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName);

public CliSymbolRenameState(
Solution solution,
Func<ISymbol, bool> predicate,
Expand Down Expand Up @@ -103,7 +113,9 @@ public override async Task RenameSymbolsAsync(Project project, CancellationToken
Document document,
CancellationToken cancellationToken)
{
LogHelpers.WriteSymbolDefinition(symbol, baseDirectoryPath: Path.GetDirectoryName(document.Project.FilePath), " ", Verbosity.Normal);
string text = DiagnosticFormatter.FormatSymbolDefinition(symbol, baseDirectoryPath: Path.GetDirectoryName(document.Project.FilePath), " ", _symbolDefinitionFormat);

WriteLine(text, ConsoleColors.Cyan, Verbosity.Normal);

if (ShouldWrite(Verbosity.Detailed)
|| CodeContext >= 0)
Expand Down
29 changes: 29 additions & 0 deletions src/Core/FileSystemHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,33 @@ public static string DetermineRelativePath(string baseDirectoryPath, string file

return directoryUri.MakeRelativeUri(baseDirectoryUri).ToString().TrimEnd('/');
}

public static int LastIndexOfDirectorySeparator(string path)
{
for (int i = path.Length - 1; i >= 0; i--)
{
if (IsDirectorySeparator(path[i]))
return i;
}

return -1;
}

public static int GetExtensionIndex(string path)
{
int length = path.Length;

for (int i = length - 1; i >= 0; i--)
{
char ch = path[i];

if (ch == '.')
return i;

if (IsDirectorySeparator(ch))
break;
}

return path.Length;
}
}
164 changes: 138 additions & 26 deletions src/Workspaces.Core/DiagnosticFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
Expand All @@ -13,28 +14,39 @@ internal static class DiagnosticFormatter
public static string FormatDiagnostic(
Diagnostic diagnostic,
string? baseDirectoryPath = null,
IFormatProvider? formatProvider = null)
IFormatProvider? formatProvider = null,
bool omitSpan = false)
{
StringBuilder sb = StringBuilderCache.GetInstance();

FormatLocation(diagnostic.Location, baseDirectoryPath, ref sb);
FormatLocation(diagnostic.Location, baseDirectoryPath, ref sb, omitSpan);

sb.Append(GetSeverityText(diagnostic.Severity));
sb.Append(' ');
sb.Append(diagnostic.Id);
sb.Append(": ");

string message = diagnostic.GetMessage(formatProvider);

sb.Append(message);
sb.Append(diagnostic.GetMessage(formatProvider));

return StringBuilderCache.GetStringAndFree(sb);

static string GetSeverityText(DiagnosticSeverity severity)
{
return severity switch
{
DiagnosticSeverity.Hidden => "hidden",
DiagnosticSeverity.Info => "info",
DiagnosticSeverity.Warning => "warning",
DiagnosticSeverity.Error => "error",
_ => throw new InvalidOperationException(),
};
}
}

internal static void FormatLocation(
private static void FormatLocation(
Location location,
string? baseDirectoryPath,
ref StringBuilder sb)
ref StringBuilder sb,
bool omitSpan = false)
{
switch (location.Kind)
{
Expand All @@ -48,34 +60,134 @@ internal static class DiagnosticFormatter
{
sb.Append(PathUtilities.TrimStart(span.Path, baseDirectoryPath));

LinePosition linePosition = span.StartLinePosition;
if (omitSpan)
{
sb.Append(": ");
}
else
{
LinePosition linePosition = span.StartLinePosition;

sb.Append('(');
sb.Append(linePosition.Line + 1);
sb.Append(',');
sb.Append(linePosition.Character + 1);
sb.Append("): ");
sb.Append('(');
sb.Append(linePosition.Line + 1);
sb.Append(',');
sb.Append(linePosition.Character + 1);
sb.Append("): ");
}
}

break;
}
}
}

private static string GetSeverityText(DiagnosticSeverity diagnosticSeverity)
public static string FormatSymbolDefinition(
ISymbol symbol,
string? baseDirectoryPath = null,
string? indentation = null,
SymbolDisplayFormat? format = null)
{
switch (diagnosticSeverity)
StringBuilder sb = StringBuilderCache.GetInstance();

sb.Append(indentation);

FormatLocation(symbol.Locations[0], baseDirectoryPath, ref sb);

sb.Append(GetSymbolTitle(symbol));

if (symbol.IsKind(SymbolKind.Parameter, SymbolKind.TypeParameter))
{
case DiagnosticSeverity.Hidden:
return "hidden";
case DiagnosticSeverity.Info:
return "info";
case DiagnosticSeverity.Warning:
return "warning";
case DiagnosticSeverity.Error:
return "error";
default:
throw new InvalidOperationException();
sb.Append(" '");
sb.Append(symbol.Name);
sb.Append("': ");

if (symbol.ContainingSymbol is IMethodSymbol { MethodKind: MethodKind.LambdaMethod })
{
sb.Append("anonymous function");
}
else
{
Debug.Assert(symbol.ContainingSymbol.IsKind(SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Property), symbol.Kind.ToString());

sb.Append(symbol.ContainingSymbol.ToDisplayString(format));
}
}
else
{
sb.Append(": ");
sb.Append(symbol.ToDisplayString(format));
}

return StringBuilderCache.GetStringAndFree(sb);

static string GetSymbolTitle(ISymbol symbol)
{
switch (symbol.Kind)
{
case SymbolKind.Event:
{
return "event";
}
case SymbolKind.Field:
{
return (symbol.ContainingType.TypeKind == TypeKind.Enum) ? "enum field" : "field";
}
case SymbolKind.Local:
{
return "local";
}
case SymbolKind.Method:
{
var methodSymbol = (IMethodSymbol)symbol;

switch (methodSymbol.MethodKind)
{
case MethodKind.Ordinary:
return "method";
case MethodKind.LocalFunction:
return "local function";
}

Debug.Fail(methodSymbol.MethodKind.ToString());
break;
}
case SymbolKind.NamedType:
{
var typeSymbol = (INamedTypeSymbol)symbol;

switch (typeSymbol.TypeKind)
{
case TypeKind.Class:
return "class";
case TypeKind.Delegate:
return "delegate";
case TypeKind.Enum:
return "enum";
case TypeKind.Interface:
return "interface";
case TypeKind.Struct:
return "struct";
}

Debug.Fail(typeSymbol.TypeKind.ToString());
break;
}
case SymbolKind.Parameter:
{
return "parameter";
}
case SymbolKind.Property:
{
return (((IPropertySymbol)symbol).IsIndexer) ? "indexer" : "property";
}
case SymbolKind.TypeParameter:
{
return "type parameter";
}
}

Debug.Fail(symbol.Kind.ToString());
return symbol.Kind.ToString();
}
}
}

0 comments on commit 8633f9e

Please sign in to comment.