diff --git a/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj b/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj
index eb20755c318..cbf108a68e4 100644
--- a/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj
+++ b/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj
@@ -607,6 +607,12 @@
Service/ExternalSymbol.fs
+
+
+ Service/QuickParse.fsi
+
+
+ Service/QuickParse.fs
Service/service.fsi
diff --git a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
index 07d9840cde2..fa10b3664ea 100644
--- a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
+++ b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
@@ -583,6 +583,12 @@
Service/ExternalSymbol.fs
+
+
+ Service/QuickParse.fsi
+
+
+ Service/QuickParse.fs
Service/service.fsi
diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
index 1cc973ee6b2..aeddec6f538 100644
--- a/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
+++ b/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
@@ -564,6 +564,12 @@
Service/ExternalSymbol.fs
+
+
+ Service/QuickParse.fsi
+
+
+ Service/QuickParse.fs
Service/service.fsi
diff --git a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
index acfb28e533b..ca673baf854 100644
--- a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
+++ b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
@@ -600,6 +600,12 @@
Service/ExternalSymbol.fs
+
+ Service/QuickParse.fsi
+
+
+ Service/QuickParse.fs
+
Service/service.fsi
diff --git a/vsintegration/src/FSharp.LanguageService/QuickParse.fs b/src/fsharp/vs/QuickParse.fs
similarity index 64%
rename from vsintegration/src/FSharp.LanguageService/QuickParse.fs
rename to src/fsharp/vs/QuickParse.fs
index 3dcb71b968c..1530759307b 100644
--- a/vsintegration/src/FSharp.LanguageService/QuickParse.fs
+++ b/src/fsharp/vs/QuickParse.fs
@@ -1,11 +1,27 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
-namespace Microsoft.VisualStudio.FSharp.LanguageService
+namespace Microsoft.FSharp.Compiler
open System
-open System.Globalization
open Microsoft.FSharp.Compiler.SourceCodeServices
+/// Qualified long name.
+type PartialLongName =
+ { /// Qualifying idents, prior to the last dot, not including the last part.
+ QualifyingIdents: string list
+
+ /// Last part of long ident.
+ PartialIdent: string
+
+ /// The column number at the end of full partial name.
+ EndColumn: int
+
+ /// Position of the last dot.
+ LastDotPos: int option }
+
+ /// Empty patial long name.
+ static member Empty(endColumn: int) = { QualifyingIdents = []; PartialIdent = ""; EndColumn = endColumn; LastDotPos = None }
+
/// Methods for cheaply and innacurately parsing F#.
///
/// These methods are very old and are mostly to do with extracting "long identifier islands"
@@ -22,15 +38,15 @@ open Microsoft.FSharp.Compiler.SourceCodeServices
/// It's also surprising how hard even the job of getting long identifier islands can be. For example the code
/// below is inaccurate for long identifier chains involving ``...`` identifiers. And there are special cases
/// for active pattern names and so on.
-module internal QuickParse =
- open Microsoft.FSharp.Compiler.SourceCodeServices.PrettyNaming
-
- let magicalAdjustmentConstant = 1 // 0 puts us immediately *before* the last character; 1 puts us after the last character
+module QuickParse =
+ open PrettyNaming
+ /// Puts us after the last character.
+ let MagicalAdjustmentConstant = 1
// Adjusts the token tag for the given identifier
// - if we're inside active pattern name (at the bar), correct the token TAG to be an identifier
- let CorrectIdentifierToken (s:string) tokenTag =
- if s.EndsWith "|" then Microsoft.FSharp.Compiler.Parser.tagOfToken (Microsoft.FSharp.Compiler.Parser.token.IDENT s)
+ let CorrectIdentifierToken (tokenText: string) (tokenTag: int) =
+ if tokenText.EndsWith "|" then Microsoft.FSharp.Compiler.Parser.tagOfToken (Microsoft.FSharp.Compiler.Parser.token.IDENT tokenText)
else tokenTag
let rec isValidStrippedName (name:string) idx =
@@ -42,7 +58,7 @@ module internal QuickParse =
// Extracts the 'core' part without surrounding bars and checks whether it contains some identifier
// (Note, this doesn't have to be precise, because this is checked by backround compiler,
// but it has to be good enough to distinguish operators and active pattern names)
- let private isValidActivePatternName (name:string) =
+ let private isValidActivePatternName (name: string) =
// Strip the surrounding bars (e.g. from "|xyz|_|") to get "xyz"
match name.StartsWith("|", System.StringComparison.Ordinal),
@@ -52,40 +68,17 @@ module internal QuickParse =
| true, _, true when name.Length > 2 -> isValidStrippedName (name.Substring(1, name.Length - 2)) 0
| _ -> false
- /// Given a string and a position in that string, find an identifier as
- /// expected by `GotoDefinition`. This will work when the cursor is
- /// immediately before the identifier, within the identifier, or immediately
- /// after the identifier.
- ///
- /// 'tolerateJustAfter' indicates that we tolerate being one character after the identifier, used
- /// for goto-definition
-
- /// In general, only identifiers composed from upper/lower letters and '.' are supported, but there
- /// are a couple of explicitly handled exceptions to allow some common scenarios:
- /// - When the name contains only letters and '|' symbol, it may be an active pattern, so we
- /// treat it as a valid identifier - e.g. let ( |Identitiy| ) a = a
- /// (but other identifiers that include '|' are not allowed - e.g. '||' operator)
- /// - It searches for double tick (``) to see if the identifier could be something like ``a b``
-
- /// REVIEW: Also support, e.g., operators, performing the necessary mangling.
- /// (i.e., I would like that the name returned here can be passed as-is
- /// (post `.`-chopping) to `GetDeclarationLocation.)
-
- /// In addition, return the position where a `.` would go if we were making
- /// a call to `DeclItemsForNamesAtPosition` for intellisense. This will
- /// allow us to use find the correct qualified items rather than resorting
- /// to the more expensive and less accurate environment lookup.
- let GetCompleteIdentifierIslandImpl (s : string) (p : int) : (string*int*bool) option =
- if p < 0 || isNull s || p >= s.Length then None
+ let GetCompleteIdentifierIslandImpl (lineStr: string) (index: int) : (string * int * bool) option =
+ if index < 0 || isNull lineStr || index >= lineStr.Length then None
else
let fixup =
match () with
// at a valid position, on a valid character
- | _ when (p < s.Length) && (s.[p] = '|' || IsIdentifierPartCharacter s.[p]) -> Some p
+ | _ when (index < lineStr.Length) && (lineStr.[index] = '|' || IsIdentifierPartCharacter lineStr.[index]) -> Some index
| _ -> None // not on a word or '.'
- let (|Char|_|) p = if p >=0 && p < s.Length then Some(s.[p]) else None
+ let (|Char|_|) p = if p >=0 && p < lineStr.Length then Some(lineStr.[p]) else None
let (|IsLongIdentifierPartChar|_|) c = if IsLongIdentifierPartCharacter c then Some () else None
let (|IsIdentifierPartChar|_|) c = if IsIdentifierPartCharacter c then Some () else None
@@ -105,7 +98,7 @@ module internal QuickParse =
let tickColsOpt =
let rec walkOutsideBackticks i =
- if i >= s.Length then None
+ if i >= lineStr.Length then None
else
match i, i + 1 with
| Char '`', Char '`' ->
@@ -113,19 +106,19 @@ module internal QuickParse =
// if pos = i then it will be included in backticked range ($``identifier``)
walkInsideBackticks (i + 2) i
| _, _ ->
- if i >= p then None
+ if i >= index then None
else
// we still not reached position p - continue walking
walkOutsideBackticks (i + 1)
and walkInsideBackticks i start =
- if i >= s.Length then None // non-closed backticks
+ if i >= lineStr.Length then None // non-closed backticks
else
match i, i + 1 with
| Char '`', Char '`' ->
// found closing pair of backticks
// if target position is between start and current pos + 1 (entire range of escaped identifier including backticks) - return success
// else climb outside and continue walking
- if p >= start && p < (i + 2) then Some (start, i)
+ if index >= start && index < (i + 2) then Some (start, i)
else walkOutsideBackticks (i + 2)
| _, _ -> walkInsideBackticks (i + 1) start
@@ -134,43 +127,66 @@ module internal QuickParse =
match tickColsOpt with
| Some (prevTickTick, idxTickTick) ->
// inside ``identifier`` (which can contain any characters!) so we try returning its location
- let pos = idxTickTick + 1 + magicalAdjustmentConstant
- let ident = s.Substring(prevTickTick, idxTickTick - prevTickTick + 2)
+ let pos = idxTickTick + 1 + MagicalAdjustmentConstant
+ let ident = lineStr.Substring(prevTickTick, idxTickTick - prevTickTick + 2)
Some(ident, pos, true)
| _ ->
// find location of an ordinary identifier
fixup |> Option.bind (fun p ->
let l = searchLeft p
let r = searchRight p
- let ident = s.Substring (l, r - l + 1)
+ let ident = lineStr.Substring (l, r - l + 1)
if ident.IndexOf('|') <> -1 && not(isValidActivePatternName(ident)) then None else
- let pos = r + magicalAdjustmentConstant
+ let pos = r + MagicalAdjustmentConstant
Some(ident, pos, false)
)
- let GetCompleteIdentifierIsland (tolerateJustAfter:bool) (s : string) (p : int) : (string*int*bool) option =
- if String.IsNullOrEmpty s then None
+ /// Given a string and a position in that string, find an identifier as
+ /// expected by `GotoDefinition`. This will work when the cursor is
+ /// immediately before the identifier, within the identifier, or immediately
+ /// after the identifier.
+ ///
+ /// 'tolerateJustAfter' indicates that we tolerate being one character after the identifier, used
+ /// for goto-definition
+ ///
+ /// In general, only identifiers composed from upper/lower letters and '.' are supported, but there
+ /// are a couple of explicitly handled exceptions to allow some common scenarios:
+ /// - When the name contains only letters and '|' symbol, it may be an active pattern, so we
+ /// treat it as a valid identifier - e.g. let ( |Identitiy| ) a = a
+ /// (but other identifiers that include '|' are not allowed - e.g. '||' operator)
+ /// - It searches for double tick (``) to see if the identifier could be something like ``a b``
+ ///
+ /// REVIEW: Also support, e.g., operators, performing the necessary mangling.
+ /// (i.e., I would like that the name returned here can be passed as-is
+ /// (post `.`-chopping) to `GetDeclarationLocation.)
+ ///
+ /// In addition, return the position where a `.` would go if we were making
+ /// a call to `DeclItemsForNamesAtPosition` for intellisense. This will
+ /// allow us to use find the correct qualified items rather than resorting
+ /// to the more expensive and less accurate environment lookup.
+ let GetCompleteIdentifierIsland (tolerateJustAfter: bool) (lineStr: string) (index: int) : (string * int * bool) option =
+ if String.IsNullOrEmpty lineStr then None
else
- let directResult = GetCompleteIdentifierIslandImpl s p
+ let directResult = GetCompleteIdentifierIslandImpl lineStr index
if tolerateJustAfter && directResult = None then
- GetCompleteIdentifierIslandImpl s (p-1)
+ GetCompleteIdentifierIslandImpl lineStr (index - 1)
else
directResult
- let private defaultName = [],""
+ let private defaultName = [], ""
/// Get the partial long name of the identifier to the left of index.
- let GetPartialLongName(line:string,index) =
- if isNull line then defaultName
+ let GetPartialLongName(lineStr: string, index: int) =
+ if isNull lineStr then defaultName
elif index < 0 then defaultName
- elif index >= line.Length then defaultName
+ elif index >= lineStr.Length then defaultName
else
- let IsIdentifierPartCharacter pos = IsIdentifierPartCharacter line.[pos]
- let IsLongIdentifierPartCharacter pos = IsLongIdentifierPartCharacter line.[pos]
- let IsDot pos = line.[pos] = '.'
+ let IsIdentifierPartCharacter pos = IsIdentifierPartCharacter lineStr.[pos]
+ let IsLongIdentifierPartCharacter pos = IsLongIdentifierPartCharacter lineStr.[pos]
+ let IsDot pos = lineStr.[pos] = '.'
let rec InLeadingIdentifier(pos,right,(prior,residue)) =
- let PushName() = ((line.Substring(pos+1,right-pos-1))::prior),residue
+ let PushName() = ((lineStr.Substring(pos+1,right-pos-1))::prior),residue
if pos < 0 then PushName()
elif IsIdentifierPartCharacter pos then InLeadingIdentifier(pos-1,right,(prior,residue))
elif IsDot pos then InLeadingIdentifier(pos-1,pos,PushName())
@@ -178,132 +194,150 @@ module internal QuickParse =
let rec InName(pos,startResidue,right) =
let NameAndResidue() =
- [line.Substring(pos+1,startResidue-pos-1)],(line.Substring(startResidue+1,right-startResidue))
- if pos < 0 then [line.Substring(pos+1,startResidue-pos-1)],(line.Substring(startResidue+1,right-startResidue))
+ [lineStr.Substring(pos+1,startResidue-pos-1)],(lineStr.Substring(startResidue+1,right-startResidue))
+ if pos < 0 then [lineStr.Substring(pos+1,startResidue-pos-1)],(lineStr.Substring(startResidue+1,right-startResidue))
elif IsIdentifierPartCharacter pos then InName(pos-1,startResidue,right)
elif IsDot pos then InLeadingIdentifier(pos-1,pos,NameAndResidue())
else NameAndResidue()
let rec InResidue(pos,right) =
- if pos < 0 then [],line.Substring(pos+1,right-pos)
+ if pos < 0 then [],lineStr.Substring(pos+1,right-pos)
elif IsDot pos then InName(pos-1,pos,right)
elif IsLongIdentifierPartCharacter pos then InResidue(pos-1, right)
- else [],line.Substring(pos+1,right-pos)
+ else [],lineStr.Substring(pos+1,right-pos)
let result = InResidue(index,index)
result
type private EatCommentCallContext =
- | SkipWhiteSpaces of string * string list * bool
- | StartIdentifier of string list * bool
+ | SkipWhiteSpaces of ident: string * current: string list * throwAwayNext: bool
+ | StartIdentifier of current: string list * throwAway: bool
/// Get the partial long name of the identifier to the left of index.
- /// For example, for `System.DateTime.Now` it returns ([|"System"; "DateTime"|], "Now").
- let GetPartialLongNameEx(line:string,index) : (string list * string) =
- if isNull line then defaultName
- elif index < 0 then defaultName
- elif index >= line.Length then defaultName
+ /// For example, for `System.DateTime.Now` it returns PartialLongName ([|"System"; "DateTime"|], "Now", Some 32), where "32" pos of the last dot.
+ let GetPartialLongNameEx(lineStr: string, index: int) : PartialLongName =
+ if isNull lineStr then PartialLongName.Empty(index)
+ elif index < 0 then PartialLongName.Empty(index)
+ elif index >= lineStr.Length then PartialLongName.Empty(index)
else
- let IsIdentifierPartCharacter pos = IsIdentifierPartCharacter line.[pos]
+ let IsIdentifierPartCharacter pos = IsIdentifierPartCharacter lineStr.[pos]
let IsIdentifierStartCharacter pos = IsIdentifierPartCharacter pos
- let IsDot pos = line.[pos] = '.'
- let IsTick pos = line.[pos] = '`'
- let IsEndOfComment pos = pos < index - 1 && line.[pos] = '*' && line.[pos + 1] = ')'
- let IsStartOfComment pos = pos < index - 1 && line.[pos] = '(' && line.[pos + 1] = '*'
- let IsWhitespace pos = Char.IsWhiteSpace(line.[pos])
+ let IsDot pos = lineStr.[pos] = '.'
+ let IsTick pos = lineStr.[pos] = '`'
+ let IsEndOfComment pos = pos < index - 1 && lineStr.[pos] = '*' && lineStr.[pos + 1] = ')'
+ let IsStartOfComment pos = pos < index - 1 && lineStr.[pos] = '(' && lineStr.[pos + 1] = '*'
+ let IsWhitespace pos = Char.IsWhiteSpace(lineStr.[pos])
- let rec SkipWhitespaceBeforeDotIdentifier(pos, ident, current,throwAwayNext) =
- if pos > index then defaultName // we're in whitespace after an identifier, if this is where the cursor is, there is no PLID here
- elif IsWhitespace pos then SkipWhitespaceBeforeDotIdentifier(pos+1,ident,current,throwAwayNext)
- elif IsDot pos then AtStartOfIdentifier(pos+1,ident::current,throwAwayNext)
- elif IsStartOfComment pos then EatComment(1, pos + 1, EatCommentCallContext.SkipWhiteSpaces(ident, current, throwAwayNext))
- else AtStartOfIdentifier(pos,[],false) // Throw away what we have and start over.
+ let rec SkipWhitespaceBeforeDotIdentifier(pos, ident, current, throwAwayNext, lastDotPos) =
+ if pos > index then PartialLongName.Empty(index) // we're in whitespace after an identifier, if this is where the cursor is, there is no PLID here
+ elif IsWhitespace pos then SkipWhitespaceBeforeDotIdentifier(pos+1,ident,current,throwAwayNext,lastDotPos)
+ elif IsDot pos then AtStartOfIdentifier(pos+1,ident::current,throwAwayNext, Some pos)
+ elif IsStartOfComment pos then EatComment(1, pos + 1, EatCommentCallContext.SkipWhiteSpaces(ident, current, throwAwayNext), lastDotPos)
+ else AtStartOfIdentifier(pos,[],false,None) // Throw away what we have and start over.
- and EatComment (nesting, pos, callContext) =
- if pos > index then defaultName else
+ and EatComment (nesting, pos, callContext,lastDotPos) =
+ if pos > index then PartialLongName.Empty(index) else
if IsStartOfComment pos then
// track balance of closing '*)'
- EatComment(nesting + 1, pos + 2, callContext)
+ EatComment(nesting + 1, pos + 2, callContext,lastDotPos)
else
if IsEndOfComment pos then
if nesting = 1 then
// all right, we are at the end of comment, jump outside
match callContext with
| EatCommentCallContext.SkipWhiteSpaces(ident, current, throwAway) ->
- SkipWhitespaceBeforeDotIdentifier(pos + 2, ident, current, throwAway)
+ SkipWhitespaceBeforeDotIdentifier(pos + 2, ident, current, throwAway,lastDotPos)
| EatCommentCallContext.StartIdentifier(current, throwAway) ->
- AtStartOfIdentifier(pos + 2, current, throwAway)
+ AtStartOfIdentifier(pos + 2, current, throwAway,lastDotPos)
else
// reduce level of nesting and continue
- EatComment(nesting - 1, pos + 2, callContext)
+ EatComment(nesting - 1, pos + 2, callContext, lastDotPos)
else
// eat next char
- EatComment(nesting, pos + 1, callContext)
+ EatComment(nesting, pos + 1, callContext, lastDotPos)
- and InUnquotedIdentifier(left:int,pos:int,current,throwAwayNext) =
+ and InUnquotedIdentifier(left:int,pos:int,current,throwAwayNext,lastDotPos) =
if pos > index then
- if throwAwayNext then defaultName else current,line.Substring(left,pos-left)
+ if throwAwayNext then
+ PartialLongName.Empty(index)
+ else
+ { QualifyingIdents = current
+ PartialIdent = lineStr.Substring(left,pos-left)
+ EndColumn = index
+ LastDotPos = lastDotPos }
else
- if IsIdentifierPartCharacter pos then InUnquotedIdentifier(left,pos+1,current,throwAwayNext)
+ if IsIdentifierPartCharacter pos then InUnquotedIdentifier(left,pos+1,current,throwAwayNext,lastDotPos)
elif IsDot pos then
- let ident = line.Substring(left,pos-left)
- AtStartOfIdentifier(pos+1,ident::current,throwAwayNext)
+ let ident = lineStr.Substring(left,pos-left)
+ AtStartOfIdentifier(pos+1,ident::current,throwAwayNext, Some pos)
elif IsWhitespace pos || IsStartOfComment pos then
- let ident = line.Substring(left,pos-left)
- SkipWhitespaceBeforeDotIdentifier(pos, ident, current,throwAwayNext)
- else AtStartOfIdentifier(pos,[],false) // Throw away what we have and start over.
+ let ident = lineStr.Substring(left,pos-left)
+ SkipWhitespaceBeforeDotIdentifier(pos, ident, current, throwAwayNext, lastDotPos)
+ else AtStartOfIdentifier(pos,[],false,None) // Throw away what we have and start over.
- and InQuotedIdentifier(left:int,pos:int, current,throwAwayNext) =
+ and InQuotedIdentifier(left:int,pos:int, current,throwAwayNext,lastDotPos) =
if pos > index then
- if throwAwayNext then defaultName else current,line.Substring(left,pos-left)
+ if throwAwayNext then
+ PartialLongName.Empty(index)
+ else
+ { QualifyingIdents = current
+ PartialIdent = lineStr.Substring(left,pos-left)
+ EndColumn = index
+ LastDotPos = lastDotPos }
else
- let remainingLength = line.Length - pos
+ let remainingLength = lineStr.Length - pos
if IsTick pos && remainingLength > 1 && IsTick(pos+1) then
- let ident = line.Substring(left, pos-left)
- SkipWhitespaceBeforeDotIdentifier(pos+2,ident,current,throwAwayNext)
- else InQuotedIdentifier(left,pos+1,current,throwAwayNext)
+ let ident = lineStr.Substring(left, pos-left)
+ SkipWhitespaceBeforeDotIdentifier(pos+2,ident,current,throwAwayNext,lastDotPos)
+ else InQuotedIdentifier(left,pos+1,current,throwAwayNext,lastDotPos)
- and AtStartOfIdentifier(pos:int, current, throwAwayNext) =
+ and AtStartOfIdentifier(pos:int, current, throwAwayNext, lastDotPos: int option) =
if pos > index then
- if throwAwayNext then defaultName else current,""
+ if throwAwayNext then
+ PartialLongName.Empty(index)
+ else
+ { QualifyingIdents = current
+ PartialIdent = ""
+ EndColumn = index
+ LastDotPos = lastDotPos }
else
- if IsWhitespace pos then AtStartOfIdentifier(pos+1,current,throwAwayNext)
+ if IsWhitespace pos then AtStartOfIdentifier(pos+1,current,throwAwayNext, lastDotPos)
else
- let remainingLength = line.Length - pos
- if IsTick pos && remainingLength > 1 && IsTick(pos+1) then InQuotedIdentifier(pos+2,pos+2,current,throwAwayNext)
- elif IsStartOfComment pos then EatComment(1, pos + 1, EatCommentCallContext.StartIdentifier(current, throwAwayNext))
- elif IsIdentifierStartCharacter pos then InUnquotedIdentifier(pos,pos+1,current,throwAwayNext)
+ let remainingLength = lineStr.Length - pos
+ if IsTick pos && remainingLength > 1 && IsTick(pos+1) then InQuotedIdentifier(pos+2,pos+2,current,throwAwayNext,lastDotPos)
+ elif IsStartOfComment pos then EatComment(1, pos + 1, EatCommentCallContext.StartIdentifier(current, throwAwayNext), lastDotPos)
+ elif IsIdentifierStartCharacter pos then InUnquotedIdentifier(pos,pos+1,current,throwAwayNext,lastDotPos)
elif IsDot pos then
if pos = 0 then
// dot on first char of line, currently treat it like empty identifier to the left
- AtStartOfIdentifier(pos+1,""::current,throwAwayNext)
+ AtStartOfIdentifier(pos+1,""::current,throwAwayNext, Some pos)
elif not (pos > 0 && (IsIdentifierPartCharacter(pos-1) || IsWhitespace(pos-1))) then
// it's not dots as part.of.a.long.ident, it's e.g. the range operator (..), or some other multi-char operator ending in dot
- if line.[pos-1] = ')' then
+ if lineStr.[pos-1] = ')' then
// one very problematic case is someCall(args).Name
// without special logic, we will decide that ). is an operator and parse Name as the plid
// but in fact this is an expression tail, and we don't want a plid, rather we need to use expression typings at that location
// so be sure not to treat the name here as a plid
- AtStartOfIdentifier(pos+1,[],true) // Throw away what we have, and the next apparent plid, and start over.
+ AtStartOfIdentifier(pos+1,[],true,None) // Throw away what we have, and the next apparent plid, and start over.
else
- AtStartOfIdentifier(pos+1,[],false) // Throw away what we have and start over.
+ AtStartOfIdentifier(pos+1,[],false,None) // Throw away what we have and start over.
else
- AtStartOfIdentifier(pos+1,""::current,throwAwayNext)
- else AtStartOfIdentifier(pos+1,[],throwAwayNext)
- let plid, residue = AtStartOfIdentifier(0,[],false)
- let plid = List.rev plid
- match plid with
- | s::_rest when s.Length > 0 && Char.IsDigit(s.[0]) -> defaultName // "2.0" is not a longId (this might not be right for ``2.0`` but good enough for common case)
- | _ -> plid, residue
+ AtStartOfIdentifier(pos+1,""::current,throwAwayNext, Some pos)
+ else AtStartOfIdentifier(pos+1,[],throwAwayNext, None)
+ let partialLongName = AtStartOfIdentifier(0, [], false, None)
+
+ match List.rev partialLongName.QualifyingIdents with
+ | s :: _ when s.Length > 0 && Char.IsDigit(s.[0]) -> PartialLongName.Empty(index) // "2.0" is not a longId (this might not be right for ``2.0`` but good enough for common case)
+ | plid -> { partialLongName with QualifyingIdents = plid }
- let TokenNameEquals (tokenInfo : FSharpTokenInfo) token2 =
+ let TokenNameEquals (tokenInfo: FSharpTokenInfo) (token2: string) =
String.Compare(tokenInfo .TokenName, token2, StringComparison.OrdinalIgnoreCase) = 0
// The prefix of the sequence of token names to look for in TestMemberOrOverrideDeclaration, in reverse order
let private expected = [ [|"dot"|]; [|"ident"|]; [|"member"; "override"|] ]
/// Tests whether the user is typing something like "member x." or "override (*comment*) x."
- let internal TestMemberOrOverrideDeclaration (tokens:FSharpTokenInfo[]) =
+ let TestMemberOrOverrideDeclaration (tokens: FSharpTokenInfo[]) =
let filteredReversed =
tokens
|> Array.filter (fun tok ->
diff --git a/src/fsharp/vs/QuickParse.fsi b/src/fsharp/vs/QuickParse.fsi
new file mode 100644
index 00000000000..78bfa2b39f3
--- /dev/null
+++ b/src/fsharp/vs/QuickParse.fsi
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.FSharp.Compiler
+
+open System
+open Microsoft.FSharp.Compiler.SourceCodeServices
+
+/// Qualified long name.
+#if COMPILER_PUBLIC_API
+type PartialLongName =
+#else
+type internal PartialLongName =
+#endif
+ { /// Qualifying idents, prior to the last dot, not including the last part.
+ QualifyingIdents: string list
+
+ /// Last part of long ident.
+ PartialIdent: string
+
+ /// The column number at the end of full partial name.
+ EndColumn: int
+
+ /// Position of the last dot.
+ LastDotPos: int option }
+
+ /// Empty patial long name.
+ static member Empty: endColumn: int -> PartialLongName
+
+/// Methods for cheaply and innacurately parsing F#.
+///
+/// These methods are very old and are mostly to do with extracting "long identifier islands"
+/// A.B.C
+/// from F# source code, an approach taken from pre-F# VS samples for implementing intelliense.
+///
+/// This code should really no longer be needed since the language service has access to
+/// parsed F# source code ASTs. However, the long identifiers are still passed back to GetDeclarations and friends in the
+/// F# Compiler Service and it's annoyingly hard to remove their use completely.
+///
+/// In general it is unlikely much progress will be made by fixing this code - it will be better to
+/// extract more information from the F# ASTs.
+///
+/// It's also surprising how hard even the job of getting long identifier islands can be. For example the code
+/// below is inaccurate for long identifier chains involving ``...`` identifiers. And there are special cases
+/// for active pattern names and so on.
+#if COMPILER_PUBLIC_API
+module QuickParse =
+#else
+module internal QuickParse =
+#endif
+ /// Puts us after the last character.
+ val MagicalAdjustmentConstant : int
+
+ // Adjusts the token tag for the given identifier
+ // - if we're inside active pattern name (at the bar), correct the token TAG to be an identifier
+ val CorrectIdentifierToken : tokenText: string -> tokenTag: int -> int
+
+ /// Given a string and a position in that string, find an identifier as
+ /// expected by `GotoDefinition`. This will work when the cursor is
+ /// immediately before the identifier, within the identifier, or immediately
+ /// after the identifier.
+ ///
+ /// 'tolerateJustAfter' indicates that we tolerate being one character after the identifier, used
+ /// for goto-definition
+ ///
+ /// In general, only identifiers composed from upper/lower letters and '.' are supported, but there
+ /// are a couple of explicitly handled exceptions to allow some common scenarios:
+ /// - When the name contains only letters and '|' symbol, it may be an active pattern, so we
+ /// treat it as a valid identifier - e.g. let ( |Identitiy| ) a = a
+ /// (but other identifiers that include '|' are not allowed - e.g. '||' operator)
+ /// - It searches for double tick (``) to see if the identifier could be something like ``a b``
+ ///
+ /// REVIEW: Also support, e.g., operators, performing the necessary mangling.
+ /// (i.e., I would like that the name returned here can be passed as-is
+ /// (post `.`-chopping) to `GetDeclarationLocation.)
+ ///
+ /// In addition, return the position where a `.` would go if we were making
+ /// a call to `DeclItemsForNamesAtPosition` for intellisense. This will
+ /// allow us to use find the correct qualified items rather than resorting
+ /// to the more expensive and less accurate environment lookup.
+ val GetCompleteIdentifierIsland : tolerateJustAfter: bool -> tokenText: string -> index: int -> (string * int * bool) option
+
+ /// Get the partial long name of the identifier to the left of index.
+ val GetPartialLongName : lineStr: string * index: int -> (string list * string)
+
+ /// Get the partial long name of the identifier to the left of index.
+ /// For example, for `System.DateTime.Now` it returns PartialLongName ([|"System"; "DateTime"|], "Now", Some 32), where "32" pos of the last dot.
+ val GetPartialLongNameEx : lineStr: string * index: int -> PartialLongName
+
+ /// Tests whether the user is typing something like "member x." or "override (*comment*) x."
+ val TestMemberOrOverrideDeclaration : tokens: FSharpTokenInfo[] -> bool
\ No newline at end of file
diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs
index 5c2fc8f5445..d387ca9702a 100644
--- a/src/fsharp/vs/service.fs
+++ b/src/fsharp/vs/service.fs
@@ -140,7 +140,7 @@ type SemanticClassificationType =
| TypeArgument
| Operator
| Disposable
-
+
/// A TypeCheckInfo represents everything we get back from the typecheck of a file.
/// It acts like an in-memory database about the file.
/// It is effectively immutable and not updated: when we re-typecheck we just drop the previous
@@ -216,7 +216,7 @@ type TypeCheckInfo
| None -> true
if contained then
- match !bestAlmostIncludedSoFar with
+ match !bestAlmostIncludedSoFar with
| Some (rightm:range,_,_) ->
if posGt possm.End rightm.End ||
(posEq possm.End rightm.End && posGt possm.Start rightm.Start) then
@@ -591,7 +591,7 @@ type TypeCheckInfo
let DefaultCompletionItem item = CompletionItem None None item
let getItem (x: ItemWithInst) = x.Item
- let GetDeclaredItems (parseResultsOpt: FSharpParseFileResults option, lineStr: string, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc,
+ let GetDeclaredItems (parseResultsOpt: FSharpParseFileResults option, lineStr: string, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc,
filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator, allSymbols: unit -> AssemblySymbol list) =
// Are the last two chars (except whitespaces) = ".."
@@ -612,18 +612,12 @@ type TypeCheckInfo
// deals with cases when we have spaces between dot and\or identifier, like A . $
// if this is our case - then we need to locate end position of the name skipping whitespaces
// this allows us to handle cases like: let x . $ = 1
-
- // colAtEndOfNamesAndResidue is 1-based so at first we need to convert it to 0-based
- //
- // TODO: this code would be a lot simpler if we just passed in colAtEndOfNames in
- // the first place. colAtEndOfNamesAndResidue serves no purpose. The cracking below is
- // inaccurate and incomplete in any case since it only works on a single line.
- match FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1) with
+ match lastDotPos |> Option.orElseWith (fun _ -> FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1)) with
| Some p when lineStr.[p] = '.' ->
match FindFirstNonWhitespacePosition lineStr (p - 1) with
| Some colAtEndOfNames ->
- let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based
- GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck)
+ let colAtEndOfNames = colAtEndOfNames + 1 // convert 0-based to 1-based
+ GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck)
| None -> NameResResult.Empty
| _ -> NameResResult.Empty
@@ -647,7 +641,7 @@ type TypeCheckInfo
match NameResolution.TryToResolveLongIdentAsType ncenv nenv m plid with
| Some x -> Some x
| None ->
- match FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1) with
+ match lastDotPos |> Option.orElseWith (fun _ -> FindFirstNonWhitespacePosition lineStr (colAtEndOfNamesAndResidue - 1)) with
| Some p when lineStr.[p] = '.' ->
match FindFirstNonWhitespacePosition lineStr (p - 1) with
| Some colAtEndOfNames ->
@@ -769,7 +763,7 @@ type TypeCheckInfo
/// Get the auto-complete items at a particular location.
let GetDeclItemsForNamesAtPosition(ctok: CompilationThreadToken, parseResultsOpt: FSharpParseFileResults option, origLongIdentOpt: string list option,
- residueOpt:string option, line:int, lineStr:string, colAtEndOfNamesAndResidue, filterCtors, resolveOverloads,
+ residueOpt:string option, lastDotPos: int option, line:int, lineStr:string, colAtEndOfNamesAndResidue, filterCtors, resolveOverloads,
getAllSymbols: unit -> AssemblySymbol list, hasTextChangedSinceLastTypecheck: (obj * range -> bool))
: (CompletionItem list * DisplayEnv * CompletionContext option * range) option =
RequireCompilationThread ctok // the operations in this method need the reactor thread
@@ -812,7 +806,7 @@ type TypeCheckInfo
match GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, plid) |> toCompletionItems with
| [],_,_ ->
// no record fields found, return completion list as if we were outside any computation expression
- GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, false, fun() -> [])
+ GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, false, fun() -> [])
| result -> Some(result)
// Completion at ' { XXX = ... with ... } "
@@ -835,7 +829,7 @@ type TypeCheckInfo
let results = GetNamedParametersAndSettableFields endPos hasTextChangedSinceLastTypecheck
let declaredItems =
- GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors, resolveOverloads,
+ GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads,
hasTextChangedSinceLastTypecheck, false, getAllSymbols)
match results with
@@ -858,7 +852,7 @@ type TypeCheckInfo
| _ -> declaredItems
| Some(CompletionContext.AttributeApplication) ->
- GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
+ GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
|> Option.map (fun (items, denv, m) ->
items
|> List.filter (fun cItem ->
@@ -868,14 +862,14 @@ type TypeCheckInfo
| _ -> false), denv, m)
| Some(CompletionContext.OpenDeclaration) ->
- GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
+ GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols)
|> Option.map (fun (items, denv, m) ->
items |> List.filter (fun x -> match x.Item with Item.ModuleOrNamespaces _ -> true | _ -> false), denv, m)
// Other completions
| cc ->
let isInRangeOperator = (match cc with Some (CompletionContext.RangeOperator) -> true | _ -> false)
- GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator, getAllSymbols)
+ GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator, getAllSymbols)
res |> Option.map (fun (items, denv, m) -> items, denv, completionContext, m)
@@ -902,11 +896,11 @@ type TypeCheckInfo
false)
/// Get the auto-complete items at a location
- member __.GetDeclarations (ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, getAllSymbols, hasTextChangedSinceLastTypecheck) =
+ member __.GetDeclarations (ctok, parseResultsOpt, line, lineStr, partialName, getAllSymbols, hasTextChangedSinceLastTypecheck) =
let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName
ErrorScope.Protect Range.range0
- (fun () ->
- match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some qualifyingNames, Some partialName, line, lineStr, colAtEndOfNamesAndResidue, ResolveTypeNamesToCtors, ResolveOverloads.Yes, getAllSymbols, hasTextChangedSinceLastTypecheck) with
+ (fun () ->
+ match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some partialName.QualifyingIdents, Some partialName.PartialIdent, partialName.LastDotPos, line, lineStr, partialName.EndColumn + 1, ResolveTypeNamesToCtors, ResolveOverloads.Yes, getAllSymbols, hasTextChangedSinceLastTypecheck) with
| None -> FSharpDeclarationListInfo.Empty
| Some (items, denv, ctx, m) ->
let items = if isInterfaceFile then items |> List.filter (fun x -> IsValidSignatureFileItem x.Item) else items
@@ -922,11 +916,11 @@ type TypeCheckInfo
FSharpDeclarationListInfo.Error msg)
/// Get the symbols for auto-complete items at a location
- member __.GetDeclarationListSymbols (ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, hasTextChangedSinceLastTypecheck) =
+ member __.GetDeclarationListSymbols (ctok, parseResultsOpt, line, lineStr, partialName, hasTextChangedSinceLastTypecheck) =
let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some qualifyingNames, Some partialName, line, lineStr, colAtEndOfNamesAndResidue, ResolveTypeNamesToCtors, ResolveOverloads.Yes, (fun () -> []), hasTextChangedSinceLastTypecheck) with
+ match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some partialName.QualifyingIdents, Some partialName.PartialIdent, partialName.LastDotPos, line, lineStr, partialName.EndColumn + 1, ResolveTypeNamesToCtors, ResolveOverloads.Yes, (fun () -> []), hasTextChangedSinceLastTypecheck) with
| None -> List.Empty
| Some (items, denv, _, m) ->
let items = if isInterfaceFile then items |> List.filter (fun x -> IsValidSignatureFileItem x.Item) else items
@@ -1028,7 +1022,7 @@ type TypeCheckInfo
let Compute() =
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition(ctok, None,Some(names),None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors,ResolveOverloads.Yes,(fun() -> []),fun _ -> false) with
+ match GetDeclItemsForNamesAtPosition(ctok, None,Some(names),None,None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors,ResolveOverloads.Yes,(fun() -> []),fun _ -> false) with
| None -> FSharpToolTipText []
| Some(items, denv, _, m) ->
FSharpToolTipText(items |> List.map (fun x -> FormatStructuredDescriptionOfItem false infoReader m denv x.ItemWithInst)))
@@ -1048,7 +1042,7 @@ type TypeCheckInfo
member __.GetF1Keyword (ctok, line, lineStr, colAtEndOfNames, names) : string option =
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition(ctok, None, Some names, None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.No,(fun() -> []), fun _ -> false) with // F1 Keywords do not distinguish between overloads
+ match GetDeclItemsForNamesAtPosition(ctok, None, Some names, None, None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.No,(fun() -> []), fun _ -> false) with // F1 Keywords do not distinguish between overloads
| None -> None
| Some (items: CompletionItem list, _,_, _) ->
match items with
@@ -1080,7 +1074,7 @@ type TypeCheckInfo
member __.GetMethods (ctok, line, lineStr, colAtEndOfNames, namesOpt) =
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition(ctok, None,namesOpt,None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors,ResolveOverloads.No,(fun() -> []),fun _ -> false) with
+ match GetDeclItemsForNamesAtPosition(ctok, None,namesOpt,None,None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors,ResolveOverloads.No,(fun() -> []),fun _ -> false) with
| None -> FSharpMethodGroup("",[| |])
| Some (items, denv, _, m) -> FSharpMethodGroup.Create(infoReader, m, denv, items |> List.map (fun x -> x.ItemWithInst)))
(fun msg ->
@@ -1090,7 +1084,7 @@ type TypeCheckInfo
member __.GetMethodsAsSymbols (ctok, line, lineStr, colAtEndOfNames, names) =
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition (ctok, None,Some(names), None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.No,(fun() -> []),fun _ -> false) with
+ match GetDeclItemsForNamesAtPosition (ctok, None,Some(names), None, None,line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.No,(fun() -> []),fun _ -> false) with
| None | Some ([],_,_,_) -> None
| Some (items, denv, _, m) ->
let allItems = items |> List.collect (fun item -> SymbolHelpers.FlattenItems g m item.Item)
@@ -1104,7 +1098,7 @@ type TypeCheckInfo
member scope.GetDeclarationLocation (ctok, line, lineStr, colAtEndOfNames, names, preferFlag) =
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition (ctok, None,Some(names), None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors,ResolveOverloads.Yes,(fun() -> []), fun _ -> false) with
+ match GetDeclItemsForNamesAtPosition (ctok, None,Some(names), None, None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors,ResolveOverloads.Yes,(fun() -> []), fun _ -> false) with
| None
| Some ([], _, _, _) -> FSharpFindDeclResult.DeclNotFound (FSharpFindDeclFailureReason.Unknown "")
| Some (item :: _, _, _, _) ->
@@ -1209,7 +1203,7 @@ type TypeCheckInfo
member scope.GetSymbolUseAtLocation (ctok, line, lineStr, colAtEndOfNames, names) =
ErrorScope.Protect Range.range0
(fun () ->
- match GetDeclItemsForNamesAtPosition (ctok, None,Some(names), None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.Yes,(fun() -> []), fun _ -> false) with
+ match GetDeclItemsForNamesAtPosition (ctok, None,Some(names), None, None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.Yes,(fun() -> []), fun _ -> false) with
| None | Some ([], _, _, _) -> None
| Some (item :: _, denv, _, m) ->
let symbol = FSharpSymbol.Create(g, thisCcu, tcImports, item.Item)
@@ -1885,16 +1879,16 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp
member info.HasFullTypeCheckInfo = details.IsSome
/// Intellisense autocompletions
- member info.GetDeclarationListInfo(parseResultsOpt, line, colAtEndOfNamesAndResidue, lineStr, qualifyingNames, partialName, getAllEntities, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) =
+ member info.GetDeclarationListInfo(parseResultsOpt, line, lineStr, partialName, getAllEntities, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) =
let userOpName = defaultArg userOpName "Unknown"
let hasTextChangedSinceLastTypecheck = defaultArg hasTextChangedSinceLastTypecheck (fun _ -> false)
reactorOp userOpName "GetDeclarations" FSharpDeclarationListInfo.Empty (fun ctok scope ->
- scope.GetDeclarations(ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, getAllEntities, hasTextChangedSinceLastTypecheck))
+ scope.GetDeclarations(ctok, parseResultsOpt, line, lineStr, partialName, getAllEntities, hasTextChangedSinceLastTypecheck))
- member info.GetDeclarationListSymbols(parseResultsOpt, line, colAtEndOfNamesAndResidue, lineStr, qualifyingNames, partialName, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) =
+ member info.GetDeclarationListSymbols(parseResultsOpt, line, lineStr, partialName, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) =
let userOpName = defaultArg userOpName "Unknown"
let hasTextChangedSinceLastTypecheck = defaultArg hasTextChangedSinceLastTypecheck (fun _ -> false)
- reactorOp userOpName "GetDeclarationListSymbols" List.empty (fun ctok scope -> scope.GetDeclarationListSymbols(ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, hasTextChangedSinceLastTypecheck))
+ reactorOp userOpName "GetDeclarationListSymbols" List.empty (fun ctok scope -> scope.GetDeclarationListSymbols(ctok, parseResultsOpt, line, lineStr, partialName, hasTextChangedSinceLastTypecheck))
/// Resolve the names at the given location to give a data tip
member info.GetStructuredToolTipText(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName: string) =
diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi
index 986b9a0e4c8..b274dac53fa 100755
--- a/src/fsharp/vs/service.fsi
+++ b/src/fsharp/vs/service.fsi
@@ -132,21 +132,23 @@ type internal FSharpCheckFileResults =
/// 'record field' locations and r.h.s. of 'range' operator a..b
///
/// The line number where the completion is happening
- /// The column number at the end of the 'names' text
- /// The long identifier to the left of the '.'
- /// The residue of a partial long identifier to the right of the '.'
- /// The residue of a partial long identifier to the right of the '.'
+ ///
+ /// Partial long name. QuickParse.GetPartialLongNameEx can be used to get it.
+ ///
///
/// The text of the line where the completion is happening. This is only used to make a couple
/// of adhoc corrections to completion accuracy (e.g. checking for "..")
///
+ ///
+ /// Function that returns all symbols from current and referenced assemblies.
+ ///
///
/// If text has been used from a captured name resolution from the typecheck, then
/// callback to the client to check if the text has changed. If it has, then give up
/// and assume that we're going to repeat the operation later on.
///
/// An optional string used for tracing compiler operations associated with this request.
- member GetDeclarationListInfo : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * colAtEndOfPartialName: int * lineText:string * qualifyingNames: string list * partialName: string * getAllSymbols: (unit -> AssemblySymbol list) * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async
+ member GetDeclarationListInfo : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * lineText:string * partialName: PartialLongName * getAllSymbols: (unit -> AssemblySymbol list) * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async
/// Get the items for a declaration list in FSharpSymbol format
///
@@ -156,21 +158,23 @@ type internal FSharpCheckFileResults =
/// 'record field' locations and r.h.s. of 'range' operator a..b
///
/// The line number where the completion is happening
- /// The column number at the end of the 'names' text
- /// The long identifier to the left of the '.'
- /// The residue of a partial long identifier to the right of the '.'
- /// The residue of a partial long identifier to the right of the '.'
+ ///
+ /// Partial long name. QuickParse.GetPartialLongNameEx can be used to get it.
+ ///
///
/// The text of the line where the completion is happening. This is only used to make a couple
/// of adhoc corrections to completion accuracy (e.g. checking for "..")
///
+ ///
+ /// Function that returns all symbols from current and referenced assemblies.
+ ///
///
/// If text has been used from a captured name resolution from the typecheck, then
/// callback to the client to check if the text has changed. If it has, then give up
/// and assume that we're going to repeat the operation later on.
///
/// An optional string used for tracing compiler operations associated with this request.
- member GetDeclarationListSymbols : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * colAtEndOfPartialName: int * lineText:string * qualifyingNames: string list * partialName: string * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async
+ member GetDeclarationListSymbols : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * lineText:string * partialName: PartialLongName * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async
/// Compute a formatted tooltip for the given location
@@ -200,7 +204,7 @@ type internal FSharpCheckFileResults =
/// The text of the line where the information is being requested.
/// The identifiers at the location where the information is being requested.
/// An optional string used for tracing compiler operations associated with this request.
- member GetF1Keyword : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async
+ member GetF1Keyword : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async
/// Compute a set of method overloads to show in a dialog relevant to the given code location.
@@ -210,7 +214,7 @@ type internal FSharpCheckFileResults =
/// The text of the line where the information is being requested.
/// The identifiers at the location where the information is being requested.
/// An optional string used for tracing compiler operations associated with this request.
- member GetMethods : line:int * colAtEndOfNames:int * lineText:string * names:string list option * ?userOpName: string -> Async
+ member GetMethods : line:int * colAtEndOfNames:int * lineText:string * names:string list option * ?userOpName: string -> Async
/// Compute a set of method overloads to show in a dialog relevant to the given code location. The resulting method overloads are returned as symbols.
/// The line number where the information is being requested.
diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs
index 8b70eeda1bc..e935737511e 100644
--- a/tests/service/EditorTests.fs
+++ b/tests/service/EditorTests.fs
@@ -86,7 +86,8 @@ let ``Intro test`` () =
let tip = typeCheckResults.GetToolTipText(4, 7, inputLines.[1], ["foo"], identToken) |> Async.RunSynchronously
// (sprintf "%A" tip).Replace("\n","") |> shouldEqual """FSharpToolTipText [Single ("val foo : unit -> unitFull name: Test.foo",None)]"""
// Get declarations (autocomplete) for a location
- let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 7, 23, inputLines.[6], [], "msg", (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
+ let partialName = { QualifyingIdents = []; PartialIdent = "msg"; EndColumn = 22; LastDotPos = None }
+ let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 7, inputLines.[6], partialName, (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
CollectionAssert.AreEquivalent(stringMethods,[ for item in decls.Items -> item.Name ])
// Get overloads of the String.Concat method
let methods = typeCheckResults.GetMethods(5, 27, inputLines.[4], Some ["String"; "Concat"]) |> Async.RunSynchronously
@@ -285,7 +286,7 @@ let ``Expression typing test`` () =
// gives the results for the string type.
//
for col in 42..43 do
- let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 2, col, inputLines.[1], [], "", (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 2, inputLines.[1], PartialLongName.Empty(col), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
let autoCompleteSet = set [ for item in decls.Items -> item.Name ]
autoCompleteSet |> shouldEqual (set stringMethods)
@@ -306,7 +307,7 @@ type Test() =
let file = "/home/user/Test.fsx"
let parseResult, typeCheckResults = parseAndCheckScript(file, input)
- let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, 21, inputLines.[3], [], "", (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(20), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
let item = decls.Items |> Array.tryFind (fun d -> d.Name = "abc")
decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true
@@ -323,7 +324,7 @@ type Test() =
let file = "/home/user/Test.fsx"
let parseResult, typeCheckResults = parseAndCheckScript(file, input)
- let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, 22, inputLines.[3], [], "", (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(21), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
let item = decls.Items |> Array.tryFind (fun d -> d.Name = "abc")
decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true
@@ -340,7 +341,7 @@ type Test() =
let file = "/home/user/Test.fsx"
let parseResult, typeCheckResults = parseAndCheckScript(file, input)
- let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, 15, inputLines.[3], [], "", (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(14), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously
decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true
[]
@@ -356,7 +357,7 @@ type Test() =
let file = "/home/user/Test.fsx"
let parseResult, typeCheckResults = parseAndCheckScript(file, input)
- let decls = typeCheckResults.GetDeclarationListSymbols(Some parseResult, 4, 21, inputLines.[3], [], "", fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListSymbols(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(20), fun _ -> false)|> Async.RunSynchronously
//decls |> List.map (fun d -> d.Head.Symbol.DisplayName) |> printfn "---> decls = %A"
decls |> Seq.exists (fun d -> d.Head.Symbol.DisplayName = "abc") |> shouldEqual true
@@ -373,7 +374,7 @@ type Test() =
let file = "/home/user/Test.fsx"
let parseResult, typeCheckResults = parseAndCheckScript(file, input)
- let decls = typeCheckResults.GetDeclarationListSymbols(Some parseResult, 4, 22, inputLines.[3], [], "", fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListSymbols(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(21), fun _ -> false)|> Async.RunSynchronously
//decls |> List.map (fun d -> d.Head.Symbol.DisplayName) |> printfn "---> decls = %A"
decls |> Seq.exists (fun d -> d.Head.Symbol.DisplayName = "abc") |> shouldEqual true
@@ -390,7 +391,7 @@ type Test() =
let file = "/home/user/Test.fsx"
let parseResult, typeCheckResults = parseAndCheckScript(file, input)
- let decls = typeCheckResults.GetDeclarationListSymbols(Some parseResult, 4, 15, inputLines.[3], [], "", fun _ -> false)|> Async.RunSynchronously
+ let decls = typeCheckResults.GetDeclarationListSymbols(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(14), fun _ -> false)|> Async.RunSynchronously
//decls |> List.map (fun d -> d.Head.Symbol.DisplayName) |> printfn "---> decls = %A"
decls |> Seq.exists (fun d -> d.Head.Symbol.DisplayName = "abc") |> shouldEqual true
diff --git a/vsintegration/Utils/LanguageServiceProfiling/Program.fs b/vsintegration/Utils/LanguageServiceProfiling/Program.fs
index bc785b787cd..5413e8b9094 100644
--- a/vsintegration/Utils/LanguageServiceProfiling/Program.fs
+++ b/vsintegration/Utils/LanguageServiceProfiling/Program.fs
@@ -164,10 +164,11 @@ let main argv =
fileResults.GetDeclarationListInfo(
Some parseResult,
completion.Position.Line,
- completion.Position.Column,
getLine (completion.Position.Line),
- completion.QualifyingNames,
- completion.PartialName,
+ { QualifyingIdents = completion.QualifyingNames
+ PartialIdent = completion.PartialName
+ EndColumn = completion.Position.Column - 1
+ LastDotPos = None },
fun() -> [])
for i in listInfo.Items do
diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs
index 99bcd091c91..b8f8ac4c4c9 100644
--- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs
+++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs
@@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis.Classification
open Microsoft.VisualStudio.FSharp.LanguageService
open Microsoft.VisualStudio.LanguageServices.Implementation.F1Help
open Microsoft.CodeAnalysis.Host.Mef
+open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.Range
open Microsoft.FSharp.Compiler.SourceCodeServices
diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs
index e30c0322b47..974796d9d20 100644
--- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs
+++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs
@@ -100,14 +100,14 @@ type internal FSharpCompletionProvider
let caretLine = textLines.GetLineFromPosition(caretPosition)
let fcsCaretLineNumber = Line.fromZ caretLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based
let caretLineColumn = caretLinePos.Character
- let qualifyingNames, partialName = QuickParse.GetPartialLongNameEx(caretLine.ToString(), caretLineColumn - 1)
+ let partialName = QuickParse.GetPartialLongNameEx(caretLine.ToString(), caretLineColumn - 1)
let getAllSymbols() =
- getAllSymbols() |> List.filter (fun entity -> entity.FullName.Contains "." && not (PrettyNaming.IsOperatorName entity.Symbol.DisplayName))
+ getAllSymbols()
+ |> List.filter (fun entity -> entity.FullName.Contains "." && not (PrettyNaming.IsOperatorName entity.Symbol.DisplayName))
- let! declarations =
- checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLineColumn, caretLine.ToString(), qualifyingNames, partialName, getAllSymbols, userOpName=userOpName) |> liftAsync
-
+ let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLine.ToString(),
+ partialName, getAllSymbols, userOpName=userOpName) |> liftAsync
let results = List()
let getKindPriority = function
@@ -186,7 +186,7 @@ type internal FSharpCompletionProvider
declarationItemsCache.Add(completionItem.DisplayText, declItem)
results.Add(completionItem))
- if results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty qualifyingNames then
+ if results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty partialName.QualifyingIdents then
let lineStr = textLines.[caretLinePos.Line].ToString()
match UntypedParseImpl.TryGetCompletionContext(Pos.fromZ caretLinePos.Line caretLinePos.Character, Some parseResults, lineStr) with
| None -> results.AddRange(keywordCompletionItems)
diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs
index 02ccfc1e6e8..432b0431052 100644
--- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs
+++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs
@@ -14,7 +14,7 @@ open Microsoft.CodeAnalysis.Editor.Implementation.Debugging
open Microsoft.CodeAnalysis.Host.Mef
open Microsoft.CodeAnalysis.Text
-open Microsoft.VisualStudio.FSharp.LanguageService
+open Microsoft.FSharp.Compiler
[]
[, FSharpConstants.FSharpLanguageName)>]
diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs
index b0b25ca6ead..ace07bf57f2 100644
--- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs
+++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs
@@ -69,11 +69,11 @@ type internal SimplifyNameDiagnosticAnalyzer() =
|> Array.Parallel.map (fun symbolUse ->
let lineStr = sourceText.Lines.[Line.toZ symbolUse.RangeAlternate.StartLine].ToString()
// for `System.DateTime.Now` it returns ([|"System"; "DateTime"|], "Now")
- let plid, name = QuickParse.GetPartialLongNameEx(lineStr, symbolUse.RangeAlternate.EndColumn - 1)
+ let partialName = QuickParse.GetPartialLongNameEx(lineStr, symbolUse.RangeAlternate.EndColumn - 1)
// `symbolUse.RangeAlternate.Start` does not point to the start of plid, it points to start of `name`,
// so we have to calculate plid's start ourselves.
- let plidStartCol = symbolUse.RangeAlternate.EndColumn - name.Length - (getPlidLength plid)
- symbolUse, plid, plidStartCol, name)
+ let plidStartCol = symbolUse.RangeAlternate.EndColumn - partialName.PartialIdent.Length - (getPlidLength partialName.QualifyingIdents)
+ symbolUse, partialName.QualifyingIdents, plidStartCol, partialName.PartialIdent)
|> Array.filter (fun (_, plid, _, name) -> name <> "" && not (List.isEmpty plid))
|> Array.groupBy (fun (symbolUse, _, plidStartCol, _) -> symbolUse.RangeAlternate.StartLine, plidStartCol)
|> Array.map (fun (_, xs) -> xs |> Array.maxBy (fun (symbolUse, _, _, _) -> symbolUse.RangeAlternate.EndColumn))
diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs
index 2ef32dd1941..840138c00b3 100644
--- a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs
+++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs
@@ -532,7 +532,7 @@ module internal Tokenizer =
| _ -> false)
|> Option.orElseWith (fun _ -> tokensUnderCursor |> List.tryFind (fun { DraftToken.Kind = k } -> k = LexerSymbolKind.Operator))
|> Option.map (fun token ->
- let plid, _ = QuickParse.GetPartialLongNameEx(lineStr, token.RightColumn)
+ let partialName = QuickParse.GetPartialLongNameEx(lineStr, token.RightColumn)
let identStr = lineStr.Substring(token.Token.LeftColumn, token.Token.FullMatchedLength)
{ Kind = token.Kind
Ident =
@@ -541,7 +541,7 @@ module internal Tokenizer =
fileName
(Range.mkPos (linePos.Line + 1) token.Token.LeftColumn)
(Range.mkPos (linePos.Line + 1) (token.RightColumn + 1)))
- FullIsland = plid @ [identStr] })
+ FullIsland = partialName.QualifyingIdents @ [identStr] })
let private getCachedSourceLineData(documentKey: DocumentId, sourceText: SourceText, position: int, fileName: string, defines: string list) =
let textLine = sourceText.Lines.GetLineFromPosition(position)
diff --git a/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj b/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj
index 96ed9e9404a..7e170f7530b 100644
--- a/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj
+++ b/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj
@@ -55,7 +55,6 @@
-
@@ -66,7 +65,6 @@
-
@@ -83,7 +81,6 @@
-
$(FSharpSourcesRoot)\..\packages\Microsoft.VisualFSharp.Msbuild.15.0.1.0.1\lib\net45\Microsoft.Build.Framework.dll
@@ -95,7 +92,7 @@
$(FSharpSourcesRoot)\..\packages\EnvDTE80.8.0.1\lib\net10\EnvDTE80.dllTrue
-
+ $(FSharpSourcesRoot)\..\packages\VSSDK.VSLangProj.7.0.4\lib\net20\VSLangProj.dllTrue
@@ -149,7 +146,7 @@
$(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Designer.Interfaces.1.1.4322\lib\microsoft.visualstudio.designer.interfaces.dll
-
+
$(FSharpSourcesRoot)\..\packages\VSSDK.VSHelp.7.0.4\lib\net20\Microsoft.VisualStudio.VSHelp.dllTrue
diff --git a/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs b/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs
index dd4f2df45be..b027528e689 100644
--- a/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs
+++ b/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs
@@ -57,7 +57,7 @@ module internal GotoDefinition =
| Some (s, colIdent, isQuoted) ->
let qualId = if isQuoted then [s] else s.Split '.' |> Array.toList // chop it up (in case it's a qualified ident)
// this is a bit irratiting: `GetTokenInfoAt` won't handle just-past-the-end, so we take the just-past-the-end position and adjust it by the `magicalAdjustmentConstant` to just-*at*-the-end
- let colIdentAdjusted = colIdent - QuickParse.magicalAdjustmentConstant
+ let colIdentAdjusted = colIdent - QuickParse.MagicalAdjustmentConstant
// Corrrect the identifier (e.g. to correctly handle active pattern names that end with "BAR" token)
let tag = colourizer.GetTokenInfoAt(VsTextLines.TextColorState(VsTextView.Buffer textView), line, colIdentAdjusted).Token
diff --git a/vsintegration/src/FSharp.LanguageService/Intellisense.fs b/vsintegration/src/FSharp.LanguageService/Intellisense.fs
index 1190822644e..60673bb7f47 100644
--- a/vsintegration/src/FSharp.LanguageService/Intellisense.fs
+++ b/vsintegration/src/FSharp.LanguageService/Intellisense.fs
@@ -526,14 +526,14 @@ type internal FSharpIntellisenseInfo_DEPRECATED
else
None
// TODO don't use QuickParse below, we have parse info available
- let plid = QuickParse.GetPartialLongNameEx(lineText, col-1)
- ignore plid // for breakpoint
+ let pname = QuickParse.GetPartialLongNameEx(lineText, col-1)
+ let _x = 1 // for breakpoint
let detectTextChange (oldTextSnapshotInfo: obj, range) =
let oldTextSnapshot = oldTextSnapshotInfo :?> ITextSnapshot
hasTextChangedSinceLastTypecheck (textSnapshot, oldTextSnapshot, Range.Range.toZ range)
- let! decls = typedResults.GetDeclarationListInfo(untypedParseInfoOpt, Range.Line.fromZ line, col, lineText, fst plid, snd plid, (fun() -> []), detectTextChange)
+ let! decls = typedResults.GetDeclarationListInfo(untypedParseInfoOpt, Range.Line.fromZ line, lineText, pname, (fun() -> []), detectTextChange)
return (new FSharpDeclarations_DEPRECATED(documentationBuilder, decls.Items, reason) :> Declarations_DEPRECATED)
else
// no TypeCheckInfo in ParseResult.
diff --git a/vsintegration/tests/Salsa/salsa.fs b/vsintegration/tests/Salsa/salsa.fs
index 5e33c81e71c..be29d4eecf2 100644
--- a/vsintegration/tests/Salsa/salsa.fs
+++ b/vsintegration/tests/Salsa/salsa.fs
@@ -25,7 +25,7 @@ open Microsoft.VisualStudio.FSharp.LanguageService
open Microsoft.VisualStudio.TextManager.Interop
open UnitTests.TestLib.Utils.FilesystemHelpers
open Microsoft.Build.Framework
-open Microsoft.FSharp.Compiler.Range
+open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.SourceCodeServices
open Microsoft.Build.Evaluation
diff --git a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs
index aca191434a8..88d22ecb293 100644
--- a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs
+++ b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs
@@ -7774,6 +7774,15 @@ let rec f l =
let bar = 1""",
marker = "(*Marker*)")
+ []
+ member public this.``ExpressionDotting.Regression.Bug3709``() =
+ this.VerifyCtrlSpaceListContainAllAtStartOfMarker(
+ fileContents = """
+ let foo = ""
+ let foo = foo.E(*marker*)n "a" """,
+ marker = "(*marker*)",
+ list = ["EndsWith"])
+
// Context project system
[]
type UsingProjectSystem() =
diff --git a/vsintegration/tests/unittests/Tests.LanguageService.GotoDefinition.fs b/vsintegration/tests/unittests/Tests.LanguageService.GotoDefinition.fs
index 8794cc6e688..f72aa5c93d8 100644
--- a/vsintegration/tests/unittests/Tests.LanguageService.GotoDefinition.fs
+++ b/vsintegration/tests/unittests/Tests.LanguageService.GotoDefinition.fs
@@ -1346,7 +1346,7 @@ type UsingMSBuild() =
member this.GetCompleteIdTest tolerate (s : string)(exp : string option) : unit =
let n = s.IndexOf '$'
let s = s.Remove (n, 1)
- match (Microsoft.VisualStudio.FSharp.LanguageService.QuickParse.GetCompleteIdentifierIsland tolerate s n, exp) with
+ match (Microsoft.FSharp.Compiler.QuickParse.GetCompleteIdentifierIsland tolerate s n, exp) with
| (Some (s1, _, _), Some s2) ->
printfn "%s" "Received result, as expected."
Assert.AreEqual (s1, s2)
diff --git a/vsintegration/tests/unittests/Tests.LanguageService.QuickParse.fs b/vsintegration/tests/unittests/Tests.LanguageService.QuickParse.fs
index 7c5be4fe7c4..7d08f8e4967 100644
--- a/vsintegration/tests/unittests/Tests.LanguageService.QuickParse.fs
+++ b/vsintegration/tests/unittests/Tests.LanguageService.QuickParse.fs
@@ -3,9 +3,8 @@
namespace Tests.LanguageService
open System
-open System.IO
open NUnit.Framework
-open Microsoft.VisualStudio.FSharp.LanguageService
+open Microsoft.FSharp.Compiler
[]
[]
@@ -22,37 +21,37 @@ type QuickParse() =
[]
member public qp.CheckGetPartialLongName() =
- let CheckAt(line,index,expected) =
- let actual = QuickParse.GetPartialLongNameEx(line,index)
- if actual <> expected then
+ let CheckAt(line, index, expected) =
+ let actual = QuickParse.GetPartialLongNameEx(line, index)
+ if (actual.QualifyingIdents, actual.PartialIdent, actual.LastDotPos) <> expected then
failwithf "Expected %A but got %A" expected actual
let Check(line,expected) =
CheckAt(line, line.Length-1, expected)
do Microsoft.FSharp.Compiler.AbstractIL.Diagnostics.setDiagnosticsChannel(Some(Console.Out));
- Check("let y = List.",(["List"], ""))
- Check("let y = List.conc",(["List"], "conc"))
- Check("let y = S", ([], "S"))
- Check("S", ([], "S"))
- Check("let y=", ([], ""))
- Check("Console.Wr", (["Console"], "Wr"))
- Check(" .", ([""], ""))
- Check(".", ([""], ""))
- Check("System.Console.Wr", (["System";"Console"],"Wr"))
- Check("let y=f'", ([], "f'"))
- Check("let y=SomeModule.f'", (["SomeModule"], "f'"))
- Check("let y=Some.OtherModule.f'", (["Some";"OtherModule"], "f'"))
- Check("let y=f'g", ([], "f'g"))
- Check("let y=SomeModule.f'g", (["SomeModule"], "f'g"))
- Check("let y=Some.OtherModule.f'g", (["Some";"OtherModule"], "f'g"))
- Check("let y=FSharp.Data.File.``msft-prices.csv``", ([], ""))
- Check("let y=FSharp.Data.File.``msft-prices.csv", (["FSharp";"Data";"File"], "msft-prices.csv"))
- Check("let y=SomeModule. f", (["SomeModule"], "f"))
- Check("let y=SomeModule .f", (["SomeModule"], "f"))
- Check("let y=SomeModule . f", (["SomeModule"], "f"))
- Check("let y=SomeModule .", (["SomeModule"], ""))
- Check("let y=SomeModule . ", (["SomeModule"], ""))
+ Check("let y = List.",(["List"], "", Some 12))
+ Check("let y = List.conc",(["List"], "conc", Some 12))
+ Check("let y = S", ([], "S", None))
+ Check("S", ([], "S", None))
+ Check("let y=", ([], "", None))
+ Check("Console.Wr", (["Console"], "Wr", Some 7))
+ Check(" .", ([""], "", Some 1))
+ Check(".", ([""], "", Some 0))
+ Check("System.Console.Wr", (["System";"Console"],"Wr", Some 14))
+ Check("let y=f'", ([], "f'", None))
+ Check("let y=SomeModule.f'", (["SomeModule"], "f'", Some 16))
+ Check("let y=Some.OtherModule.f'", (["Some";"OtherModule"], "f'", Some 22))
+ Check("let y=f'g", ([], "f'g", None))
+ Check("let y=SomeModule.f'g", (["SomeModule"], "f'g", Some 16))
+ Check("let y=Some.OtherModule.f'g", (["Some";"OtherModule"], "f'g", Some 22))
+ Check("let y=FSharp.Data.File.``msft-prices.csv``", ([], "", None))
+ Check("let y=FSharp.Data.File.``msft-prices.csv", (["FSharp";"Data";"File"], "msft-prices.csv", Some 22))
+ Check("let y=SomeModule. f", (["SomeModule"], "f", Some 16))
+ Check("let y=SomeModule .f", (["SomeModule"], "f", Some 18))
+ Check("let y=SomeModule . f", (["SomeModule"], "f", Some 18))
+ Check("let y=SomeModule .", (["SomeModule"], "", Some 18))
+ Check("let y=SomeModule . ", (["SomeModule"], "", Some 18))
[]