218 lines
6.7 KiB
Go
218 lines
6.7 KiB
Go
package ls
|
|
|
|
import (
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/core"
|
|
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
|
"github.com/microsoft/typescript-go/internal/printer"
|
|
"github.com/microsoft/typescript-go/internal/stringutil"
|
|
)
|
|
|
|
var _ printer.EmitTextWriter = &displayPartsWriter{}
|
|
|
|
// displayPartsWriter implements EmitTextWriter and captures classified text runs
|
|
// for VS colorized labels, while also building a plain string.
|
|
// When vsCapability is false, only the plain string is built; runs are skipped.
|
|
type displayPartsWriter struct {
|
|
builder strings.Builder
|
|
runs []*lsproto.VSClassifiedTextRun
|
|
vsCapability bool
|
|
lastWritten string
|
|
}
|
|
|
|
func newDisplayPartsWriter(vsCapability bool) *displayPartsWriter {
|
|
return &displayPartsWriter{vsCapability: vsCapability}
|
|
}
|
|
|
|
func (w *displayPartsWriter) addRun(classification lsproto.ClassificationTypeName, text string) {
|
|
if text == "" {
|
|
return
|
|
}
|
|
if w.vsCapability {
|
|
w.runs = append(w.runs, &lsproto.VSClassifiedTextRun{
|
|
ClassificationTypeName: string(classification),
|
|
Text: text,
|
|
})
|
|
}
|
|
w.lastWritten = text
|
|
w.builder.WriteString(text)
|
|
}
|
|
|
|
// WriteClassified writes text with an explicit classification type.
|
|
func (w *displayPartsWriter) WriteClassified(text string, classification lsproto.ClassificationTypeName) {
|
|
w.addRun(classification, text)
|
|
}
|
|
|
|
// WriteFrom copies the accumulated content from another displayPartsWriter.
|
|
func (w *displayPartsWriter) WriteFrom(other *displayPartsWriter) {
|
|
w.builder.WriteString(other.String())
|
|
if w.vsCapability {
|
|
w.runs = append(w.runs, other.GetRuns()...)
|
|
}
|
|
if other.lastWritten != "" {
|
|
w.lastWritten = other.lastWritten
|
|
}
|
|
}
|
|
|
|
func (w *displayPartsWriter) GetRuns() []*lsproto.VSClassifiedTextRun {
|
|
return w.runs
|
|
}
|
|
|
|
func (w *displayPartsWriter) String() string {
|
|
return w.builder.String()
|
|
}
|
|
|
|
func (w *displayPartsWriter) Clear() {
|
|
w.lastWritten = ""
|
|
w.builder.Reset()
|
|
w.runs = nil
|
|
}
|
|
|
|
func (w displayPartsWriter) DecreaseIndent() {}
|
|
|
|
func (w displayPartsWriter) GetColumn() core.UTF16Offset { return 0 }
|
|
|
|
func (w displayPartsWriter) GetIndent() int { return 0 }
|
|
|
|
func (w displayPartsWriter) GetLine() int { return 0 }
|
|
|
|
func (w displayPartsWriter) GetTextPos() int {
|
|
return w.builder.Len()
|
|
}
|
|
|
|
func (w displayPartsWriter) HasTrailingComment() bool { return false }
|
|
|
|
func (w displayPartsWriter) HasTrailingWhitespace() bool {
|
|
if w.builder.Len() == 0 {
|
|
return false
|
|
}
|
|
ch, _ := utf8.DecodeLastRuneInString(w.lastWritten)
|
|
if ch == utf8.RuneError {
|
|
return false
|
|
}
|
|
return stringutil.IsWhiteSpaceLike(ch)
|
|
}
|
|
|
|
func (w displayPartsWriter) IncreaseIndent() {}
|
|
|
|
func (w displayPartsWriter) IsAtStartOfLine() bool { return false }
|
|
|
|
func (w *displayPartsWriter) RawWrite(s string) {
|
|
w.addRun(lsproto.ClassificationTypeNameText, s)
|
|
}
|
|
|
|
func (w *displayPartsWriter) Write(s string) {
|
|
w.addRun(lsproto.ClassificationTypeNameText, s)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteComment(text string) {
|
|
// Strada's writeComment uses unknownWrite → SymbolDisplayPartKind.text → "text"
|
|
w.addRun(lsproto.ClassificationTypeNameText, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteKeyword(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNameKeyword, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteLine() {
|
|
w.addRun(lsproto.ClassificationTypeNameWhiteSpace, " ")
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteLineForce(force bool) {
|
|
w.addRun(lsproto.ClassificationTypeNameWhiteSpace, " ")
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteLiteral(s string) {
|
|
// Strada's writeLiteral → SymbolDisplayPartKind.stringLiteral → "string"
|
|
w.addRun(lsproto.ClassificationTypeNameString, s)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteOperator(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNameOperator, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteParameter(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNameParameterName, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteProperty(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNamePropertyName, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WritePunctuation(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNamePunctuation, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteSpace(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNameWhiteSpace, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteStringLiteral(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNameString, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteSymbol(text string, symbol *ast.Symbol) {
|
|
classification := classificationForSymbol(symbol)
|
|
w.addRun(classification, text)
|
|
}
|
|
|
|
func (w *displayPartsWriter) WriteTrailingSemicolon(text string) {
|
|
w.addRun(lsproto.ClassificationTypeNamePunctuation, text)
|
|
}
|
|
|
|
// classificationForSymbol determines the Roslyn classification type name based on a symbol's flags.
|
|
// Matches the Strada translation chain: displayPartKind() → GetClassificationName().
|
|
func classificationForSymbol(symbol *ast.Symbol) lsproto.ClassificationTypeName {
|
|
if symbol == nil {
|
|
return lsproto.ClassificationTypeNameText
|
|
}
|
|
flags := symbol.Flags
|
|
switch {
|
|
case flags&ast.SymbolFlagsVariable != 0:
|
|
if isFirstDeclarationOfSymbolParameter(symbol) {
|
|
return lsproto.ClassificationTypeNameParameterName
|
|
}
|
|
return lsproto.ClassificationTypeNameLocalName
|
|
case flags&ast.SymbolFlagsProperty != 0:
|
|
return lsproto.ClassificationTypeNamePropertyName
|
|
case flags&ast.SymbolFlagsGetAccessor != 0:
|
|
return lsproto.ClassificationTypeNamePropertyName
|
|
case flags&ast.SymbolFlagsSetAccessor != 0:
|
|
return lsproto.ClassificationTypeNamePropertyName
|
|
case flags&ast.SymbolFlagsEnumMember != 0:
|
|
return lsproto.ClassificationTypeNameFieldName
|
|
case flags&ast.SymbolFlagsFunction != 0:
|
|
return lsproto.ClassificationTypeNameMethodName
|
|
case flags&ast.SymbolFlagsClass != 0:
|
|
return lsproto.ClassificationTypeNameClassName
|
|
case flags&ast.SymbolFlagsInterface != 0:
|
|
return lsproto.ClassificationTypeNameInterfaceName
|
|
case flags&ast.SymbolFlagsEnum != 0:
|
|
return lsproto.ClassificationTypeNameEnumName
|
|
case flags&ast.SymbolFlagsModule != 0:
|
|
return lsproto.ClassificationTypeNameModuleName
|
|
case flags&ast.SymbolFlagsMethod != 0:
|
|
return lsproto.ClassificationTypeNameMethodName
|
|
case flags&ast.SymbolFlagsTypeParameter != 0:
|
|
return lsproto.ClassificationTypeNameTypeParameterName
|
|
case flags&ast.SymbolFlagsTypeAlias != 0:
|
|
return lsproto.ClassificationTypeNameIdentifier
|
|
case flags&ast.SymbolFlagsAlias != 0:
|
|
return lsproto.ClassificationTypeNameIdentifier
|
|
default:
|
|
return lsproto.ClassificationTypeNameText
|
|
}
|
|
}
|
|
|
|
// isFirstDeclarationOfSymbolParameter checks if the symbol's first declaration is a parameter.
|
|
func isFirstDeclarationOfSymbolParameter(symbol *ast.Symbol) bool {
|
|
declarations := symbol.Declarations
|
|
if len(declarations) == 0 {
|
|
return false
|
|
}
|
|
return declarations[0].Kind == ast.KindParameter
|
|
}
|