File: Extensions\LspFactory.cs
Web Access
Project: src\src\Razor\src\Razor\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj (Microsoft.CodeAnalysis.Razor.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
 
namespace Roslyn.LanguageServer.Protocol;
 
internal static class LspFactory
{
    private static readonly Position s_defaultPosition = new(0, 0);
 
    private static readonly LspRange s_defaultRange = new()
    {
        Start = s_defaultPosition,
        End = s_defaultPosition
    };
 
    private static readonly Position s_undefinedPosition = new(-1, -1);
 
    private static readonly LspRange s_undefinedRange = new()
    {
        Start = s_undefinedPosition,
        End = s_undefinedPosition
    };
 
    /// <summary>
    ///  Returns a <see cref="Position"/> for line 0 and character 0.
    /// </summary>
    public static Position DefaultPosition
    {
        get
        {
            var defaultPosition = s_defaultPosition;
 
            // Since Position is mutable, it's possible that something might modify it. If that happens, we should know!
            Debug.Assert(
                defaultPosition.Line == 0 &&
                defaultPosition.Character == 0,
                $"{nameof(LspFactory)}.{nameof(DefaultPosition)} has been corrupted. Current value: {defaultPosition.ToDisplayString()}");
 
            return defaultPosition;
        }
    }
 
    /// <summary>
    ///  Returns a <see cref="Position"/> for starting line 0 and character 0,
    ///  and ending line 0 and character 0.
    /// </summary>
    public static LspRange DefaultRange
    {
        get
        {
            var defaultRange = s_defaultRange;
 
            // Since Range is mutable, it's possible that something might modify it. If that happens, we should know!
            Debug.Assert(
                defaultRange.Start.Line == 0 &&
                defaultRange.Start.Character == 0 &&
                defaultRange.End.Line == 0 &&
                defaultRange.End.Character == 0,
                $"{nameof(LspFactory)}.{nameof(DefaultRange)} has been corrupted. Current value: {defaultRange.ToDisplayString()}");
 
            return defaultRange;
        }
    }
 
    public static Position UndefinedPosition
    {
        get
        {
            var undefinedPosition = s_undefinedPosition;
 
            // Since Position is mutable, it's possible that something might modify it. If that happens, we should know!
            Debug.Assert(
                undefinedPosition.Line == -1 &&
                undefinedPosition.Character == -1,
                $"{nameof(LspFactory)}.{nameof(UndefinedPosition)} has been corrupted. Current value: {undefinedPosition.ToDisplayString()}");
 
            return undefinedPosition;
        }
    }
 
    public static LspRange UndefinedRange
    {
        get
        {
            var undefinedRange = s_undefinedRange;
 
            // Since Range is mutable, it's possible that something might modify it. If that happens, we should know!
            Debug.Assert(
                undefinedRange.Start.Line == -1 &&
                undefinedRange.Start.Character == -1 &&
                undefinedRange.End.Line == -1 &&
                undefinedRange.End.Character == -1,
                $"{nameof(LspFactory)}.{nameof(UndefinedRange)} has been corrupted. Current value: {undefinedRange.ToDisplayString()}");
 
            return undefinedRange;
        }
    }
 
    public static Position CreatePosition(int line, int character)
        => (line, character) switch
        {
            (0, 0) => DefaultPosition,
            (-1, -1) => UndefinedPosition,
            _ => new(line, character)
        };
 
    public static Position CreatePosition(LinePosition linePosition)
        => CreatePosition(linePosition.Line, linePosition.Character);
 
    public static Position CreatePosition((int line, int character) position)
        => CreatePosition(position.line, position.character);
 
    public static LspRange CreateRange(int startLine, int startCharacter, int endLine, int endCharacter)
        => startLine == endLine && startCharacter == endCharacter
            ? CreateZeroWidthRange(startLine, startCharacter)
            : CreateRange(CreatePosition(startLine, startCharacter), CreatePosition(endLine, endCharacter));
 
    public static LspRange CreateRange(Position start, Position end)
        => new() { Start = start, End = end };
 
    public static LspRange CreateRange(LinePosition start, LinePosition end)
        => CreateRange(start.Line, start.Character, end.Line, end.Character);
 
    public static LspRange CreateRange((int line, int character) start, (int line, int character) end)
        => CreateRange(start.line, start.character, end.line, end.character);
 
    public static LspRange CreateRange(LinePositionSpan span)
        => CreateRange(span.Start, span.End);
 
    public static LspRange CreateZeroWidthRange(int line, int character)
        => (line, character) switch
        {
            (0, 0) => DefaultRange,
            (-1, -1) => UndefinedRange,
            _ => CreateZeroWidthRange(CreatePosition(line, character))
        };
 
    public static LspRange CreateZeroWidthRange(Position position)
        => CreateRange(position, position);
 
    public static LspRange CreateZeroWidthRange(LinePosition position)
        => CreateRange(position, position);
 
    public static LspRange CreateZeroWidthRange((int line, int character) position)
        => CreateRange(position, position);
 
    public static LspRange CreateSingleLineRange(int line, int character, int length)
        => CreateRange(line, character, line, character + length);
 
    public static LspRange CreateSingleLineRange(Position start, int length)
        => CreateRange(start, CreatePosition(start.Line, start.Character + length));
 
    public static LspRange CreateSingleLineRange(LinePosition start, int length)
        => CreateSingleLineRange(start.Line, start.Character, length);
 
    public static LspRange CreateSingleLineRange((int line, int character) start, int length)
        => CreateRange(CreatePosition(start), CreatePosition(start.line, start.character + length));
 
    public static LspLocation CreateLocation(string filePath, LinePositionSpan span)
        => CreateLocation(CreateFilePathUri(filePath), CreateRange(span));
 
    public static LspLocation CreateLocation(Uri uri, LinePositionSpan span)
        => CreateLocation(uri, CreateRange(span));
 
    public static LspLocation CreateLocation(string filePath, LspRange range)
        => CreateLocation(CreateFilePathUri(filePath), range);
 
    public static LspLocation CreateLocation(Uri uri, LspRange range)
        => new() { DocumentUri = new(uri), Range = range };
 
    public static DocumentLink CreateDocumentLink(Uri target, LspRange range)
        => new() { DocumentTarget = new(target), Range = range };
 
    public static DocumentLink CreateDocumentLink(Uri target, LinePositionSpan span)
        => new() { DocumentTarget = new(target), Range = CreateRange(span) };
 
    public static TextEdit CreateTextEdit(Range range, string newText)
        => new() { Range = range, NewText = newText };
 
    public static TextEdit CreateTextEdit(LinePositionSpan span, string newText)
        => CreateTextEdit(CreateRange(span), newText);
 
    public static TextEdit CreateTextEdit(int startLine, int startCharacter, int endLine, int endCharacter, string newText)
        => CreateTextEdit(CreateRange(startLine, startCharacter, endLine, endCharacter), newText);
 
    public static TextEdit CreateTextEdit(Position start, Position end, string newText)
        => CreateTextEdit(CreateRange(start, end), newText);
 
    public static TextEdit CreateTextEdit(LinePosition start, LinePosition end, string newText)
        => CreateTextEdit(CreateRange(start, end), newText);
 
    public static TextEdit CreateTextEdit(int line, int character, string newText)
        => CreateTextEdit(CreateZeroWidthRange(line, character), newText);
 
    public static TextEdit CreateTextEdit(Position position, string newText)
        => CreateTextEdit(CreateZeroWidthRange(position), newText);
 
    public static TextEdit CreateTextEdit(LinePosition position, string newText)
        => CreateTextEdit(CreateZeroWidthRange(position.Line, position.Character), newText);
 
    public static TextEdit CreateTextEdit((int line, int character) position, string newText)
        => CreateTextEdit(CreateZeroWidthRange(position), newText);
 
    public static Uri CreateFilePathUri(string filePath, LanguageServerFeatureOptions options)
    {
        // VS Code in Windows expects path to start with '/'
        var updateFilePath = options.ReturnCodeActionAndRenamePathsWithPrefixedSlash && !filePath.StartsWith('/')
            ? $"/{filePath}"
            : filePath;
 
        return CreateFilePathUri(updateFilePath);
    }
 
    public static Uri CreateFilePathUri(string filePath)
    {
        var builder = new UriBuilder
        {
            Path = filePath,
            Scheme = Uri.UriSchemeFile,
            Host = string.Empty,
        };
 
        return builder.Uri;
    }
 
    public static FoldingRange CreateFoldingRange(FoldingRangeKind kind, LinePositionSpan linePositionSpan)
        => new()
        {
            Kind = kind,
            StartLine = linePositionSpan.Start.Line,
            StartCharacter = linePositionSpan.Start.Character,
            EndLine = linePositionSpan.End.Line,
            EndCharacter = linePositionSpan.End.Character
        };
}