From a1126d496584913526ac53c63fde8620a8c4eafc Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Fri, 7 Apr 2017 20:57:49 +0300 Subject: [PATCH] Fix quick info for active pattern definition (#2809) * fix quick info for active pattern definition * all features work on individual active pattern results again --- .../CodeFix/AddOpenCodeFixProvider.fs | 2 +- .../ImplementInterfaceCodeFixProvider.fs | 2 +- .../DocumentHighlightsService.fs | 2 +- .../InlineRename/InlineRenameService.fs | 2 +- .../LanguageService/SymbolHelpers.fs | 2 +- .../LanguageService/Tokenizer.fs | 52 ++++++++++++++++--- .../Navigation/FindUsagesService.fs | 2 +- .../Navigation/GoToDefinitionService.fs | 8 +-- .../QuickInfo/QuickInfoProvider.fs | 6 +-- 9 files changed, 59 insertions(+), 19 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 985cf9220..bea6b7812 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -101,7 +101,7 @@ type internal FSharpAddOpenCodeFixProvider let! symbol = asyncMaybe { - let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, SymbolLookupKind.Greedy, false) return! checkResults.GetSymbolUseAtLocation(Line.fromZ linePos.Line, lexerSymbol.Ident.idRange.EndColumn, line.ToString(), lexerSymbol.FullIsland) } |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index 04344a6eb..f7853c170 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -169,7 +169,7 @@ type internal FSharpImplementInterfaceCodeFixProvider | _ -> Some context.Span.End let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput - let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy, false) let fcsTextLineNumber = textLine.LineNumber + 1 let lineContents = textLine.ToString() let! options = context.Document.GetOptionsAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index 2d37d0253..bf31a7506 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -56,7 +56,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 204b37edf..7e07a21a1 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -150,7 +150,7 @@ type internal InlineRenameService let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 11b8ad5a8..cf241cf20 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -65,7 +65,7 @@ module internal SymbolHelpers = do! Option.guard (originalText.Length > 0) let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs index 9d4cc69a5..17245bf98 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs @@ -23,6 +23,7 @@ type internal LexerSymbolKind = | Punctuation | GenericTypeParameter | StaticallyResolvedTypeParameter + | ActivePattern | Other type internal LexerSymbol = @@ -388,12 +389,22 @@ module internal Tokenizer = { Kind = kind; Token = token; RightColumn = token.LeftColumn + token.FullMatchedLength - 1 } /// Returns symbol at a given position. - let private getSymbolFromTokens (fileName: string, tokens: FSharpTokenInfo list, linePos: LinePosition, lineStr: string, lookupKind: SymbolLookupKind) : LexerSymbol option = + let private getSymbolFromTokens + ( + fileName: string, + tokens: FSharpTokenInfo list, + linePos: LinePosition, + lineStr: string, + lookupKind: SymbolLookupKind, + wholeActivePatterns: bool + ) + : LexerSymbol option = + let isIdentifier t = t.CharClass = FSharpTokenCharKind.Identifier let isOperator t = t.ColorClass = FSharpTokenColorKind.Operator let isPunctuation t = t.ColorClass = FSharpTokenColorKind.Punctuation - let inline (|GenericTypeParameterPrefix|StaticallyResolvedTypeParameterPrefix|Other|) (token: FSharpTokenInfo) = + let inline (|GenericTypeParameterPrefix|StaticallyResolvedTypeParameterPrefix|ActivePattern|Other|) (token: FSharpTokenInfo) = if token.Tag = FSharpTokenTag.QUOTE then GenericTypeParameterPrefix elif token.Tag = FSharpTokenTag.INFIX_AT_HAT_OP then // The lexer return INFIX_AT_HAT_OP token for both "^" and "@" symbols. @@ -401,6 +412,10 @@ module internal Tokenizer = if token.FullMatchedLength = 1 && lineStr.[token.LeftColumn] = '^' then StaticallyResolvedTypeParameterPrefix else Other + elif token.Tag = FSharpTokenTag.LPAREN then + if token.FullMatchedLength = 1 && lineStr.[token.LeftColumn+1] = '|' then + ActivePattern + else Other else Other // Operators: Filter out overlapped operators (>>= operator is tokenized as three distinct tokens: GREATER, GREATER, EQUALS. @@ -419,11 +434,22 @@ module internal Tokenizer = |> List.foldi (fun (acc, lastToken) index (token: FSharpTokenInfo) -> match lastToken with | Some t when token.LeftColumn <= t.RightColumn -> acc, lastToken + | Some ({ Kind = LexerSymbolKind.ActivePattern } as lastToken) when + wholeActivePatterns && + (token.Tag = FSharpTokenTag.BAR || token.Tag = FSharpTokenTag.IDENT || token.Tag = FSharpTokenTag.UNDERSCORE) -> + + let mergedToken = + {lastToken.Token with Tag = FSharpTokenTag.IDENT + RightColumn = token.RightColumn + FullMatchedLength = lastToken.Token.FullMatchedLength + token.FullMatchedLength } + + acc, Some { lastToken with Token = mergedToken; RightColumn = lastToken.RightColumn + token.FullMatchedLength } | _ -> let isLastToken = index = tokensCount - 1 match token with | GenericTypeParameterPrefix when not isLastToken -> acc, Some (DraftToken.Create LexerSymbolKind.GenericTypeParameter token) | StaticallyResolvedTypeParameterPrefix when not isLastToken -> acc, Some (DraftToken.Create LexerSymbolKind.StaticallyResolvedTypeParameter token) + | ActivePattern when wholeActivePatterns -> acc, Some (DraftToken.Create LexerSymbolKind.ActivePattern token) | _ -> let draftToken = match lastToken with @@ -434,6 +460,8 @@ module internal Tokenizer = | Some { Kind = LexerSymbolKind.StaticallyResolvedTypeParameter } -> DraftToken.Create LexerSymbolKind.Operator { token with LeftColumn = token.LeftColumn - 1 FullMatchedLength = 1 } + | Some ( { Kind = LexerSymbolKind.ActivePattern } as ap) when wholeActivePatterns && token.Tag = FSharpTokenTag.RPAREN -> + DraftToken.Create LexerSymbolKind.Ident ap.Token | _ -> let kind = if isOperator token then LexerSymbolKind.Operator @@ -459,7 +487,8 @@ module internal Tokenizer = tokensUnderCursor |> List.tryFind (fun { DraftToken.Kind = k } -> match k with - | LexerSymbolKind.Ident + | LexerSymbolKind.Ident + | LexerSymbolKind.ActivePattern | LexerSymbolKind.GenericTypeParameter | LexerSymbolKind.StaticallyResolvedTypeParameter -> true | _ -> false) @@ -519,10 +548,21 @@ module internal Tokenizer = Assert.Exception(ex) [] - let getSymbolAtPosition(documentKey: DocumentId, sourceText: SourceText, position: int, fileName: string, defines: string list, lookupKind: SymbolLookupKind) : LexerSymbol option = + let getSymbolAtPosition + ( + documentKey: DocumentId, + sourceText: SourceText, + position: int, + fileName: string, + defines: string list, + lookupKind: SymbolLookupKind, + wholeActivePatterns: bool + ) + : LexerSymbol option = + try let lineData, textLinePos, lineContents = getCachedSourceLineData(documentKey, sourceText, position, fileName, defines) - getSymbolFromTokens(fileName, lineData.Tokens, textLinePos, lineContents, lookupKind) + getSymbolFromTokens(fileName, lineData.Tokens, textLinePos, lineContents, lookupKind, wholeActivePatterns) with | :? System.OperationCanceledException -> reraise() | ex -> @@ -582,7 +622,7 @@ module internal Tokenizer = | LexerSymbolKind.Punctuation, _ -> PrettyNaming.IsPunctuation name | LexerSymbolKind.GenericTypeParameter, _ -> isGenericTypeParameter name | LexerSymbolKind.StaticallyResolvedTypeParameter, _ -> isStaticallyResolvedTypeParameter name - | (LexerSymbolKind.Ident | LexerSymbolKind.Other), _ -> + | (LexerSymbolKind.Ident | LexerSymbolKind.ActivePattern | LexerSymbolKind.Other), _ -> match symbol with | :? FSharpEntity as e when e.IsClass || e.IsFSharpRecord || e.IsFSharpUnion || e.IsValueType || e.IsFSharpModule || e.IsInterface -> isTypeNameIdent name | _ -> isFixableIdentifier name diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index c418ce920..f4c9c3f61 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -54,7 +54,7 @@ type internal FSharpFindUsagesService let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, options.OtherOptions |> Seq.toList) - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) let! declaration = checkFileResults.GetDeclarationLocationAlternate (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) |> liftAsync let tags = GlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index a099b7486..c2d4699d0 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -45,7 +45,7 @@ module internal FSharpGoToDefinition = let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = preferSignature) @@ -84,7 +84,7 @@ module internal FSharpGoToDefinition = let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, projectOptions.OtherOptions |> Seq.toList) let originTextSpan = RoslynHelpers.FSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start - let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line @@ -245,7 +245,7 @@ type internal FSharpGoToDefinitionService [] let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) |> Async.RunSynchronously @@ -278,7 +278,7 @@ type internal FSharpGoToDefinitionService [] let! _, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument (originDocument, projectOptions, allowStaleResults=true, sourceText=sourceText) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) let idRange = lexerSymbol.Ident.idRange let! declarations = diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index d27341a11..e3cf8c0d7 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -114,7 +114,7 @@ module private FSharpQuickInfo = // project options need to be retrieved because the signature file could be in another project let! extProjectOptions = projectInfoManager.TryGetOptionsForProject extDocId.ProjectId let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing (extDocument.FilePath, List.ofSeq extProjectOptions.OtherOptions) - let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy) + let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true) let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument,extProjectOptions,allowStaleResults=true,sourceText=extSourceText) let! extTooltipText = @@ -149,7 +149,7 @@ module private FSharpQuickInfo = let! sourceText = document.GetTextAsync cancellationToken let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, projectOptions.OtherOptions |> Seq.toList) - let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true) let idRange = lexerSymbol.Ident.idRange let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText) let textLinePos = sourceText.Lines.GetLinePosition position @@ -305,7 +305,7 @@ type internal FSharpQuickInfoProvider let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, options.OtherOptions |> Seq.toList) - let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise) + let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true) let! res = checkFileResults.GetStructuredToolTipTextAlternate (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT) |> liftAsync match res with | FSharpToolTipText [] -- GitLab