public static PatternMatch Merge(this PatternMatch match1, PatternMatch match2, PatternMatchMergeStrategy strategy) { PatternMatchKind kind; if (strategy == PatternMatchMergeStrategy.Simple) { // Do some intelligent merging, since both matches came from the same string. kind = MergeMatchKinds(match1.Kind, match2.Kind); } else { // Give the worst kind of match, since these are relating to different containers. kind = match1.Kind.CompareTo(match2.Kind) > 0 ? match1.Kind : match2.Kind; } // Give punctuation stripped if either has it stripped var punctuation = match1.IsPunctuationStripped || match2.IsPunctuationStripped; // Give case sensitivity only if both are case sensitive var caseSensitive = match1.IsCaseSensitive && match2.IsCaseSensitive; // Give spans from both var spans = MergeSpans(match1, match2); return(new PatternMatch(kind, punctuation, caseSensitive, spans)); }
public static Task <bool> GracefulStop(this IActorRef target, TimeSpan timeout, object stopMessage) { var internalTarget = target.AsInstanceOf <IInternalActorRef>(); var promiseRef = PromiseActorRef.Apply(internalTarget.Provider, timeout, target, stopMessage.GetType().Name); internalTarget.SendSystemMessage(new Watch(internalTarget, promiseRef)); target.Tell(stopMessage, ActorRefs.NoSender); return(promiseRef.Result.ContinueWith(t => { var returnResult = false; PatternMatch.Match(t.Result) .With <Terminated>(terminated => { returnResult = (terminated.ActorRef.Path.Equals(target.Path)); }) .Default(m => { returnResult = false; }); internalTarget.SendSystemMessage(new Unwatch(internalTarget, promiseRef)); return returnResult; }, TaskContinuationOptions.ExecuteSynchronously)); }
public PatternSignature( IReadOnlyList <string> argumentValues, string pathValue, PatternMatch declaration) : this(argumentValues, pathValue, null, declaration) { }
/// <summary> /// Finds the first match for the pattern. /// </summary> /// <param name="s"></param> /// <returns></returns> public PatternMatch Match(string s) { // Note: need to implement captures here. PatternMatch match = new PatternMatch(); computeThread.Join(); try { int j = i; for (int f = 0; f < funcs.Count;) { PatternFunc func = funcs[f]; bool applies = func(s, ref i, funcs.ElementAtOrDefault(f - 1), funcs.ElementAtOrDefault(f + 1)); if (applies) { f++; } else { f = 0; j++; i = j; } } } catch (IndexOutOfRangeException) { return(null); } return(match); }
public ComponentData(bool draw, int id, Vector3 vertex, PatternMatch match) { this.draw = draw; this.id = id; this.vertex = vertex; this.match = new PatternMatch(match.invx, match.invy, Mathf.Clamp(match.shift, 0, 5)); }
public void 文字列() { Assert.IsTrue(PatternMatch.IsMatch("abcde", "abcde")); Assert.IsFalse(PatternMatch.IsMatch("abcde", "fghijk")); Assert.IsFalse(PatternMatch.IsMatch("abcd", "abcde")); Assert.IsFalse(PatternMatch.IsMatch("abcde", "abcd")); }
public void はてな() { Assert.IsTrue(PatternMatch.IsMatch("abcde?", "abcdef")); Assert.IsTrue(PatternMatch.IsMatch("abc?ef", "abcdef")); Assert.IsTrue(PatternMatch.IsMatch("?????", "12345")); Assert.IsFalse(PatternMatch.IsMatch("abc?ef", "abccdef")); Assert.IsFalse(PatternMatch.IsMatch("?????", "123456")); }
private int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { // First see how the two items compare in a case insensitive fashion. Matches that // are strictly better (ignoring case) should prioritize the item. i.e. if we have // a prefix match, that should always be better than a substring match. // // The reason we ignore case is that it's very common for people to type expecting // completion to fix up their casing. i.e. 'false' will be written with the // expectation that it will get fixed by the completion list to 'False'. var diff = match1.CompareTo(match2, ignoreCase: true); if (diff != 0) { return(diff); } // If they both seemed just as good, but they differ on preselection, then // item1 is better if it is preselected, otherwise it is worse. if (item1.Rules.MatchPriority == MatchPriority.Preselect && item2.Rules.MatchPriority != MatchPriority.Preselect) { return(-1); } else if (item1.Rules.MatchPriority != MatchPriority.Preselect && item2.Rules.MatchPriority == MatchPriority.Preselect) { return(1); } // At this point we have two items which we're matching in a rather similar fasion. // If one is a prefix of the other, prefer the prefix. i.e. if we have // "Table" and "table:=" and the user types 't' and we are in a case insensitive // language, then we prefer the former. if (item1.DisplayText.Length != item2.DisplayText.Length) { var comparison = _isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; if (item2.DisplayText.StartsWith(item1.DisplayText, comparison)) { return(-1); } else if (item1.DisplayText.StartsWith(item2.DisplayText, comparison)) { return(1); } } // Now compare the matches again in a case sensitive manner. If everything was // equal up to this point, we prefer the item that better matches based on case. diff = match1.CompareTo(match2, ignoreCase: false); if (diff != 0) { return(diff); } return(0); }
private static ImmutableArray <Span> MergeSpans(PatternMatch match1, PatternMatch match2) { var collection1 = new NormalizedSpanCollection(match1.MatchedSpans); var collection2 = new NormalizedSpanCollection(match2.MatchedSpans); var builder = ArrayBuilder <Span> .GetInstance(); builder.AddRange(NormalizedSpanCollection.Union(collection1, (collection2))); return(builder.ToImmutable()); }
private int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { // First see how the two items compare in a case insensitive fashion. Matches that // are strictly better (ignoring case) should prioritize the item. i.e. if we have // a prefix match, that should always be better than a substring match. // // The reason we ignore case is that it's very common for people to type expecting // completion to fix up their casing. i.e. 'false' will be written with the // expectation that it will get fixed by the completion list to 'False'. var diff = match1.CompareTo(match2, ignoreCase: true); if (diff != 0) { return(diff); } // Now, after comparing matches, check if an item wants to be preselected. If so, // we prefer that. i.e. say the user has typed 'f' and we have the items 'foo' // and 'False' (with the latter being 'Preselected'). Both will be a prefix match. // And because we are ignoring case, neither will be seen as better. Now, because // 'False' is preselected we pick it even though 'foo' matches 'f' case sensitively. diff = item2.Rules.MatchPriority - item1.Rules.MatchPriority; if (diff != 0) { return(diff); } // At this point we have two items which we're matching in a rather similar fasion. // If one is a prefix of the other, prefer the prefix. i.e. if we have // "Table" and "table:=" and the user types 't' and we are in a case insensitive // language, then we prefer the former. if (item1.DisplayText.Length != item2.DisplayText.Length) { var comparison = _isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; if (item2.DisplayText.StartsWith(item1.DisplayText, comparison)) { return(-1); } else if (item1.DisplayText.StartsWith(item2.DisplayText, comparison)) { return(1); } } // Now compare the matches again in a case sensitive manner. If everything was // equal up to this point, we prefer the item that better matches based on case. diff = match1.CompareTo(match2, ignoreCase: false); if (diff != 0) { return(diff); } return(0); }
public void あすたりすく() { Assert.IsTrue(PatternMatch.IsMatch("abcde*", "abcdef")); Assert.IsTrue(PatternMatch.IsMatch("*def*", "abcdefghi")); Assert.IsTrue(PatternMatch.IsMatch("*abc", "abc")); Assert.IsTrue(PatternMatch.IsMatch("*", "123456")); Assert.IsFalse(PatternMatch.IsMatch("*abc", "bc")); Assert.IsFalse(PatternMatch.IsMatch("*abc*", "ac")); Assert.IsFalse(PatternMatch.IsMatch("*abc*", "ab")); }
private PatternSignature( IReadOnlyList <string> argumentValues, string pathValue, string body, PatternMatch declaration) { this.ArgumentValues = argumentValues; this.PathValue = pathValue; this.Body = body; this.Declaration = declaration; }
protected override void OnReceive(object message) { PatternMatch.Match(message) .With <ListenerMessage>(l => Listeners.ListenerReceive(l)) .With <string>(s => { if (s.Equals("foo")) { Listeners.Gossip("bar"); } }); }
protected virtual string TransformText(string str, ScenarioContext context) { if (string.IsNullOrEmpty(str)) { return(str); } var match = PatternMatch.Parse(str); return(match == null ? str : TransformText(match.ReplaceMatched(TransformPattern(match.MatchedPattern, context)), context)); }
protected override void OnReceive(object message) { PatternMatch.Match(message) .With <RegisterTransportActor>(r => { /* * TODO: need to add support for RemoteDispatcher here. * See https://github.com/akka/akka/blob/master/akka-remote/src/main/scala/akka/remote/RemoteSettings.scala#L42 * and https://github.com/akka/akka/blob/master/akka-remote/src/main/scala/akka/remote/Remoting.scala#L95 */ Sender.Tell(Context.ActorOf(r.Props.WithDeploy(Deploy.Local), r.Name)); }); }
public int Call(string topic, object[] args) { var listenCount = 0; var rootEvent = false; if (bookedEvents == null) { rootEvent = true; bookedEvents = new List <Action>(); } foreach (ISubscribeEvent se in subscribeEvents) { if (se.Muting) { continue; } var match = false; foreach (string t in se.Topics) { if (PatternMatch.IsMatch(t, topic)) { match = true; break; } } if (!match) { continue; } listenCount++; se.Call(topic, args); } if (rootEvent) { var copied = bookedEvents.ToArray(); bookedEvents = null; foreach (Action e in copied) { e(); } } return(listenCount); }
protected int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { int diff; diff = PatternMatch.CompareType(match1, match2); if (diff != 0) { return(diff); } diff = PatternMatch.CompareCamelCase(match1, match2); if (diff != 0) { return(diff); } // argument names are not prefered if (IsArgumentName(item1) && !IsArgumentName(item2)) { return(1); } else if (IsArgumentName(item2) && !IsArgumentName(item1)) { return(-1); } // preselected items are prefered if (item1.Rules.Preselect && !item2.Rules.Preselect) { return(-1); } else if (item2.Rules.Preselect && !item1.Rules.Preselect) { return(1); } diff = PatternMatch.CompareCase(match1, match2); if (diff != 0) { return(diff); } diff = PatternMatch.ComparePunctuation(match1, match2); if (diff != 0) { return(diff); } return(0); }
/// <summary> /// Find all markers in the datagram /// </summary> /// <param name="data"></param> /// <param name="markers"></param> /// <returns></returns> private static FoundMarker FindDatagramMarker(IEnumerable <byte> data, IEnumerable <Tuple <byte[], Marker> > markers) { var patternMatchResults = new List <FoundMarker>(); var aCollection = data as byte[] ?? data.ToArray(); var firstOffset = 0; foreach (var marker in markers) { var offset = marker.Item2.OffsetInStream; var result = PatternMatch <byte> .MatchOrMatchPartiallyAtEnd(aCollection, marker.Item1, firstOffset + offset); if (!result.NoMatch) { if (!patternMatchResults.Any()) { firstOffset = result.MatchPos - offset; } patternMatchResults.Add(new FoundMarker(marker.Item1, result, marker.Item2)); } else { break; //if we have no marker, then } } if (patternMatchResults.Count == 0) { return(null); } if (patternMatchResults.Count > 1) { // sort results: first the full matches then the partial matches, then by position patternMatchResults.Sort((m1, m2) => { if (m1.Result.FullMatch && !m2.Result.FullMatch) { return(-1); } if (m2.Result.FullMatch && !m1.Result.FullMatch) { return(1); } return(m1.Result.MatchPos - m2.Result.MatchPos); }); } return(patternMatchResults.First()); }
/// <summary> /// By default, <see cref="Failure"/> is logged at error level and other /// reason types are not logged. It is possible to override this behavior. /// </summary> /// <param name="reason"></param> protected virtual void LogTermination(Reason reason) { PatternMatch.Match(reason) .With <Failure>(f => { if (f.Cause is Exception) { _log.Error(f.Cause.AsInstanceOf <Exception>(), "terminating due to Failure"); } else { _log.Error(f.Cause.ToString()); } }); }
protected override void OnReceive(object message) { PatternMatch.Match(message) .With <InitializeLogger>(m => Sender.Tell(new LoggerInitialized())) .With <Error>(m => Trace.TraceError(m.ToString())) .With <Warning>(m => Trace.TraceWarning(m.ToString())) .With <DeadLetter>(m => Trace.TraceWarning(string.Format("Deadletter - unable to send message {0} from {1} to {2}", m.Message, m.Sender, m.Sender), typeof(DeadLetter).ToString())) .With <UnhandledMessage>(m => Trace.TraceWarning(string.Format("Unhandled message!"), typeof(UnhandledMessage).ToString())) .Default(m => { if (m != null) { Trace.TraceInformation(m.ToString()); } }); }
protected override void OnReceive(object message) { PatternMatch.Match(message) .With <string>(str => { if (str.Equals("bar")) { _barCount.GetAndIncrement(); _barLatch.CountDown(); } if (str.Equals("foo")) { _fooLatch.CountDown(); } }); }
public static Vector3 TransformVertex(Vector3 vertex, Vector3 refl, PatternMatch match) { //reflect x if (match.invx) { float y = vertex.y; vertex = (2 * Vector3.Dot(refl, vertex) * refl) - vertex; vertex = new Vector3(vertex.x, y, vertex.z); } //reflect y if (match.invy) { vertex = Vector3.Scale(new Vector3(1, -1, 1), vertex); } //rotate vertex = Vx.AxiRot[match.shift] * vertex; return(vertex); }
private void ReportMatchResult(Project project, INavigateToSearchResult result) { var matchedSpans = result.NameMatchSpans.SelectAsArray(t => t.ToSpan()); var patternMatch = new PatternMatch(GetPatternMatchKind(result.MatchKind), punctuationStripped: true, result.IsCaseSensitive, matchedSpans); var navigateToItem = new NavigateToItem( result.Name, result.Kind, GetNavigateToLanguage(project.Language), result.SecondarySort, result, patternMatch, _displayFactory); _callback.AddItem(navigateToItem); }
public static Task <bool> GracefulStop(this ActorRef target, TimeSpan timeout, object stopMessage) { var internalTarget = target.AsInstanceOf <InternalActorRef>(); if (internalTarget.IsTerminated) { return(Task.Run(() => true)); } var provider = Futures.ResolveProvider(target); var promise = new TaskCompletionSource <object>(); //set up the timeout var cancellationSource = new CancellationTokenSource(); cancellationSource.Token.Register(() => promise.TrySetCanceled()); cancellationSource.CancelAfter(timeout); //create a new tempcontainer path var path = provider.TempPath(); //callback to unregister from tempcontainer Action unregister = () => provider.UnregisterTempActor(path); var fref = new FutureActorRef(promise, ActorRef.NoSender, unregister, path); internalTarget.Tell(new Watch(internalTarget, fref)); target.Tell(stopMessage, ActorRef.NoSender); return(promise.Task.ContinueWith(t => { var returnResult = false; PatternMatch.Match(t.Result) .With <Terminated>(terminated => { returnResult = (terminated.ActorRef.Path.Equals(target.Path)); }) .Default(m => { returnResult = false; }); internalTarget.Tell(new Unwatch(target, fref)); return returnResult; }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.AttachedToParent)); }
public void Mute(string topic, bool mute = true) { if (bookedEvents != null) { bookedEvents.Add(() => Mute(topic, mute)); return; } for (int i = subscribeEvents.Count - 1; i >= 0; --i) { foreach (string t in subscribeEvents[i].Topics) { if (PatternMatch.IsMatch(t, topic)) { subscribeEvents[i].Muting = mute; } } } }
protected override void OnReceive(object message) { PatternMatch.Match(message) .With <InitializeLogger>(m => { var bus = m.LoggingBus; bus.Subscribe(this.Self, typeof(SetTarget)); bus.Subscribe(this.Self, typeof(UnhandledMessage)); Sender.Tell(new LoggerInitialized()); }) .With <SetTarget>(m => { dst = m.Ref; dst.Tell("OK"); }) .With <LogEvent>(m => dst.Tell(m)) .With <UnhandledMessage>(m => dst.Tell(m)); }
private void IntersectPatternMatches(IList <PatternMatch> remains, PatternMatch[] newMatches) { for (int r = remains.Count - 1; r >= 0; r--) { bool intersects = false; foreach (var m in newMatches) { if (remains[r].index == m.index) { intersects = true; remains[r] = new PatternMatch(m.index, Math.Min(remains[r].score, m.score)); } } if (!intersects) { remains.RemoveAt(r); } } }
private int VertexCast(Vector2 viewpos) { Ray ray = _previewutility.camera.ViewportPointToRay(viewpos); if (ray.direction == Vector3.zero || target == null || target.vertices == null) { return(-1); } PatternMatch match = new PatternMatch(invx, invy, 0); float nearest_t = Mathf.Infinity; int nearest_sphere = -1; float near_clip = _previewutility.camera.nearClipPlane; float far_clip = _previewutility.camera.farClipPlane; List <Vector3> vertices = target.vertices; for (int k = 0; k < vertices.Count; k++) { Vector3 vertex = Vx.TransformVertex(vertices[k], Vx.ReflVector, match); Vector3 point_vector = ray.origin - vertex; float proj_pv = Vector3.Dot(ray.direction, point_vector); float sqr_magn = Vector3.SqrMagnitude(point_vector - (proj_pv * ray.direction)); //check if ray intersects point if (sqr_magn > VxlGUI.POINT_RADIUSSQR) { continue; } //get t of impact and check if in range float t = Mathf.Abs(proj_pv) - Mathf.Sqrt(Mathf.Max(0, VxlGUI.POINT_RADIUSSQR - sqr_magn)); if (t < near_clip || t > far_clip) { continue; } if (t >= nearest_t) { continue; } nearest_t = t; nearest_sphere = k; } return(nearest_sphere); }
public void EggsInBasket() { var eggs = 2; var basket = PatternMatch.Match() .With(() => eggs == 0, "No eggs") .With(() => eggs == 1, "One egg") .With(() => eggs > 1, string.Format("{0} eggs", eggs)) .Else("Invalid number of eggs"); var twoEggs = basket.Do(); eggs = 0; var zeroEggs = basket.Do(); eggs = int.MinValue; var invalidEggs = basket.Do(); twoEggs.ShouldBe("2 eggs"); zeroEggs.ShouldBe("No eggs"); invalidEggs.ShouldBe("Invalid number of eggs"); }
protected override void OnReceive(object message) { PatternMatch.Match(message) .With <Spawn>(m => { Context.ActorOf(Props.Create(() => new KillableActor(testActor)), m.Name); testActor.Tell(Tuple.Create("Created", m.Name)); }) .With <ContextStop>(m => { var child = Context.Child(m.Name); Context.Stop(child); }) .With <Stop>(m => { var child = Context.Child(m.Name); child.Stop(); }) .With <Count>(m => testActor.Tell(Context.GetChildren().Count())); }
private void FindAndReplaceBytes() { if (Result == FileResult.Undefined) { PatternMatch patternMatch = new PatternMatch(oldData, Bytes); if (!patternMatch.Success) { Logger.Info("{0} The dimensions provided were not found anywhere in the file.", LogPrefix); Bytes = new byte[0]; Result = FileResult.PatternNotFound; } else { Logger.Info("{0} Success", LogPrefix); Result = FileResult.Success; Bytes[patternMatch.Index] = newData[0]; Bytes[patternMatch.Index + 1] = newData[1]; Bytes[patternMatch.Index + 2] = newData[2]; Bytes[patternMatch.Index + 3] = newData[3]; } } }
protected virtual int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { return match1.CompareTo(match2); }
/// <summary> /// Internal helper for MatchPatternInternal /// </summary> /// <remarks> /// PERF: Designed to minimize allocations in common cases. /// If there's no match, then null is returned. /// If there's a single match, or the caller only wants the first match, then it is returned (as a Nullable) /// If there are multiple matches, and the caller wants them all, then a List is allocated. /// </remarks> /// <param name="candidate">The word being tested.</param> /// <param name="segment">The segment of the pattern to check against the candidate.</param> /// <param name="wantAllMatches">Does the caller want all matches or just the first?</param> /// <param name="allMatches">If <paramref name="wantAllMatches"/> is true, and there's more than one match, then the list of all matches.</param> /// <returns>If there's only one match, then the return value is that match. Otherwise it is null.</returns> private PatternMatch? MatchSegment(string candidate, Segment segment, bool wantAllMatches, out PatternMatch[] allMatches) { allMatches = null; // First check if the segment matches as is. This is also useful if the segment contains // characters we would normally strip when splitting into parts that we also may want to // match in the candidate. For example if the segment is "@int" and the candidate is // "@int", then that will show up as an exact match here. // // Note: if the segment contains a space or an asterisk then we must assume that it's a // multi-word segment. if (!ContainsSpaceOrAsterisk(segment.TotalTextChunk.Text)) { var match = MatchTextChunk(candidate, segment.TotalTextChunk, punctuationStripped: false); if (match != null) { return match; } } // The logic for pattern matching is now as follows: // // 1) Break the segment passed in into words. Breaking is rather simple and a // good way to think about it that if gives you all the individual alphanumeric words // of the pattern. // // 2) For each word try to match the word against the candidate value. // // 3) Matching is as follows: // // a) Check if the word matches the candidate entirely, in an case insensitive or // sensitive manner. If it does, return that there was an exact match. // // b) Check if the word is a prefix of the candidate, in a case insensitive or // sensitive manner. If it does, return that there was a prefix match. // // c) If the word is entirely lowercase, then check if it is contained anywhere in the // candidate in a case insensitive manner. If so, return that there was a substring // match. // // Note: We only have a substring match if the lowercase part is prefix match of // some word part. That way we don't match something like 'Class' when the user // types 'a'. But we would match 'FooAttribute' (since 'Attribute' starts with // 'a'). // // d) If the word was not entirely lowercase, then check if it is contained in the // candidate in a case *sensitive* manner. If so, return that there was a substring // match. // // e) If the word was not entirely lowercase, then attempt a camel cased match as // well. // // f) The word is all lower case. Is it a case insensitive substring of the candidate starting // on a part boundary of the candidate? // // Only if all words have some sort of match is the pattern considered matched. var subWordTextChunks = segment.SubWordTextChunks; PatternMatch[] matches = null; for (int i = 0; i < subWordTextChunks.Length; i++) { var subWordTextChunk = subWordTextChunks[i]; // Try to match the candidate with this word var result = MatchTextChunk(candidate, subWordTextChunk, punctuationStripped: true); if (result == null) { return null; } if (!wantAllMatches || subWordTextChunks.Length == 1) { // Stop at the first word return result; } matches = matches ?? new PatternMatch[subWordTextChunks.Length]; matches[i] = result.Value; } allMatches = matches; return null; }
protected int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { int diff; diff = PatternMatch.CompareType(match1, match2); if (diff != 0) { return diff; } diff = PatternMatch.CompareCamelCase(match1, match2); if (diff != 0) { return diff; } // argument names are not prefered if (IsArgumentName(item1) && !IsArgumentName(item2)) { return 1; } else if (IsArgumentName(item2) && !IsArgumentName(item1)) { return -1; } // preselected items are prefered if (item1.Rules.Preselect && !item2.Rules.Preselect) { return -1; } else if (item2.Rules.Preselect && !item1.Rules.Preselect) { return 1; } diff = PatternMatch.CompareCase(match1, match2); if (diff != 0) { return diff; } diff = PatternMatch.ComparePunctuation(match1, match2); if (diff != 0) { return diff; } return 0; }
private int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { // First see how the two items compare in a case insensitive fashion. Matches that // are strictly better (ignoring case) should prioritize the item. i.e. if we have // a prefix match, that should always be better than a substring match. // // The reason we ignore case is that it's very common for people to type expecting // completion to fix up their casing. i.e. 'false' will be written with the // expectation that it will get fixed by the completion list to 'False'. var diff = match1.CompareTo(match2, ignoreCase: true); if (diff != 0) { return diff; } // If they both seemed just as good, but they differ on preselection, then // item1 is better if it is preselected, otherwise it is worse. if (item1.Rules.MatchPriority == MatchPriority.Preselect && item2.Rules.MatchPriority != MatchPriority.Preselect) { return -1; } else if (item1.Rules.MatchPriority != MatchPriority.Preselect && item2.Rules.MatchPriority == MatchPriority.Preselect) { return 1; } // At this point we have two items which we're matching in a rather similar fasion. // If one is a prefix of the other, prefer the prefix. i.e. if we have // "Table" and "table:=" and the user types 't' and we are in a case insensitive // language, then we prefer the former. if (item1.DisplayText.Length != item2.DisplayText.Length) { var comparison = _isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; if (item2.DisplayText.StartsWith(item1.DisplayText, comparison)) { return -1; } else if (item1.DisplayText.StartsWith(item2.DisplayText, comparison)) { return 1; } } // Now compare the matches again in a case sensitive manner. If everything was // equal up to this point, we prefer the item that better matches based on case. diff = match1.CompareTo(match2, ignoreCase: false); if (diff != 0) { return diff; } return 0; }
/// <summary> /// Internal helper for MatchPatternInternal /// </summary> /// <remarks> /// PERF: Designed to minimize allocations in common cases. /// If there's no match, then null is returned. /// If there's a single match, or the caller only wants the first match, then it is returned (as a Nullable) /// If there are multiple matches, and the caller wants them all, then a List is allocated. /// </remarks> /// <param name="candidate">The word being tested.</param> /// <param name="pattern">The multiple-word query pattern.</param> /// <param name="wantAllMatches">Does the caller want all matches or just the first?</param> /// <param name="allMatches">If <paramref name="wantAllMatches"/> is true, and there's more than one match, then the list of all matches.</param> /// <returns>If there's only one match, then the return value is that match. Otherwise it is null.</returns> private PatternMatch? MatchPatternInternal(string candidate, string pattern, bool wantAllMatches, out PatternMatch[] allMatches) { allMatches = null; // We never match whitespace only if (string.IsNullOrWhiteSpace(pattern) || string.IsNullOrWhiteSpace(candidate)) { return null; } // First check if the pattern matches as is. This is also useful if the pattern contains // characters we would normally strip when splitting into parts that we also may want to // match in the candidate. For example if the pattern is "@int" and the candidate is // "@int", then that will show up as an exact match here. // // Note: if the pattern contains a space or an asterisk then we must assume that it's a // multi-word pattern. if (!ContainsSpaceOrAsterisk(pattern)) { var match = MatchSingleWordPattern(candidate, pattern, punctuationStripped: false); if (match != null) { return match; } } var patternParts = GetPatternParts(pattern); PatternMatch[] matches = null; for (int i = 0; i < patternParts.Length; i++) { var word = patternParts[i]; // Try to match the candidate with this word var result = MatchSingleWordPattern(candidate, word, punctuationStripped: true); if (result == null) { return null; } if (!wantAllMatches || patternParts.Length == 1) { // Stop at the first word return result; } if (matches == null) { matches = new PatternMatch[patternParts.Length]; } matches[i] = result.Value; } allMatches = matches; return null; }
private int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { // First see how the two items compare in a case insensitive fashion. Matches that // are strictly better (ignoring case) should prioritize the item. i.e. if we have // a prefix match, that should always be better than a substring match. // // The reason we ignore case is that it's very common for people to type expecting // completion to fix up their casing. i.e. 'false' will be written with the // expectation that it will get fixed by the completion list to 'False'. var diff = match1.CompareTo(match2, ignoreCase: true); if (diff != 0) { return diff; } // Now, after comparing matches, check if an item wants to be preselected. If so, // we prefer that. i.e. say the user has typed 'f' and we have the items 'foo' // and 'False' (with the latter being 'Preselected'). Both will be a prefix match. // And because we are ignoring case, neither will be seen as better. Now, because // 'False' is preselected we pick it even though 'foo' matches 'f' case sensitively. diff = item2.Rules.MatchPriority - item1.Rules.MatchPriority; if (diff != 0) { return diff; } // At this point we have two items which we're matching in a rather similar fasion. // If one is a prefix of the other, prefer the prefix. i.e. if we have // "Table" and "table:=" and the user types 't' and we are in a case insensitive // language, then we prefer the former. if (item1.DisplayText.Length != item2.DisplayText.Length) { var comparison = _isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; if (item2.DisplayText.StartsWith(item1.DisplayText, comparison)) { return -1; } else if (item1.DisplayText.StartsWith(item2.DisplayText, comparison)) { return 1; } } // Now compare the matches again in a case sensitive manner. If everything was // equal up to this point, we prefer the item that better matches based on case. diff = match1.CompareTo(match2, ignoreCase: false); if (diff != 0) { return diff; } return 0; }