-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP - DO NOT MERGE - Spike for F# support #392
base: 203
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net461</TargetFramework> | ||
<RootNamespace>JetBrains.ReSharper.Azure.FSharp</RootNamespace> | ||
<AssemblyName>JetBrains.ReSharper.Azure.FSharp</AssemblyName> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="JetBrains.Rider.SDK" Version="$(RiderSDKVersion)" /> | ||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net461" Version="1.0.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Azure.Psi\Azure.Psi.csproj" /> | ||
<ProjectReference Include="..\Azure.Daemon\Azure.Daemon.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Reference Include="FSharp.Compiler.Service, Version=2020.3.0.0, Culture=neutral, PublicKeyToken=3099b8d9d20e74bf"> | ||
<HintPath>..\..\..\build\dependencies\dotnet\FSharp.Compiler.Service.dll</HintPath> | ||
</Reference> | ||
<Reference Include="JetBrains.ReSharper.Plugins.FSharp.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=333a98252ac829ae"> | ||
<HintPath>..\..\..\build\dependencies\dotnet\JetBrains.ReSharper.Plugins.FSharp.Common.dll</HintPath> | ||
</Reference> | ||
<Reference Include="JetBrains.ReSharper.Plugins.FSharp.ProjectModelBase, Version=1.0.0.0, Culture=neutral, PublicKeyToken=333a98252ac829ae"> | ||
<HintPath>..\..\..\build\dependencies\dotnet\JetBrains.ReSharper.Plugins.FSharp.ProjectModelBase.dll</HintPath> | ||
</Reference> | ||
<Reference Include="JetBrains.ReSharper.Plugins.FSharp.Psi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=333a98252ac829ae"> | ||
<HintPath>..\..\..\build\dependencies\dotnet\JetBrains.ReSharper.Plugins.FSharp.Psi.dll</HintPath> | ||
</Reference> | ||
<Reference Include="JetBrains.ReSharper.Plugins.FSharp.Psi.Features, Version=1.0.0.0, Culture=neutral, PublicKeyToken=333a98252ac829ae"> | ||
<HintPath>..\..\..\build\dependencies\dotnet\JetBrains.ReSharper.Plugins.FSharp.Psi.Features.dll</HintPath> | ||
</Reference> | ||
</ItemGroup> | ||
|
||
<ItemGroup Label="F#"> | ||
<ErrorsGen Include="Errors\FunctionAppErrors.xml"> | ||
<Namespace>JetBrains.ReSharper.Azure.FSharp.Errors.FunctionAppErrors</Namespace> | ||
<OutputFile>Errors\FunctionAppErrors.Generated.cs</OutputFile> | ||
<Mode>ERRORS</Mode> | ||
</ErrorsGen> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Errors language="C#" configurableSeverityImplementationLanguage="FSHARP"> | ||
|
||
<Usings> | ||
JetBrains.ReSharper.Azure.Daemon.Highlighters; | ||
JetBrains.ReSharper.Azure.FSharp.Errors; | ||
JetBrains.ReSharper.Azure.FSharp.FunctionApp.Stages.Analysis; | ||
JetBrains.ReSharper.Plugins.FSharp.Psi; | ||
JetBrains.ReSharper.Plugins.FSharp.Psi.Tree; | ||
</Usings> | ||
|
||
<!-- Register the static severity groups. This is mostly used in grouping items in SWEA results. | ||
The groups should be different to any configurable severity groups --> | ||
<StaticSeverityGroups> | ||
<Group name="Azure Errors" key="AzureErrors" /> | ||
<Group name="Azure Warnings" key="AzureWarnings" /> | ||
<Group name="Azure Gutter Marks" key="AzureGutterMarks" /> | ||
</StaticSeverityGroups> | ||
|
||
<!-- Register the configurable severities | ||
Can take a child Tag or Group element | ||
* Tag is configurable severity without a group | ||
* Group[@name] specifies the name of the group, expects child Tag elements. | ||
Note that the group name/title should be registered with | ||
[assembly: RegisterConfigurableHighlightingsGroup(name, "Title")] | ||
* Tag[@name] is the HIGHLIGHTING_ID of a warning/error. It is treated as a string | ||
literal and quoted. It is referenced in Warning[@configurableSeverity] | ||
* Tag[@default] is the default severity. Just the enum value, no leading "Severity." | ||
* Tag[@type] is optional. Default is "simple". Other options include "global", | ||
"localAndGlobal" | ||
* Tag[@externalName] is also the HIGHLIGHTING_ID, but is treated verbatim, rather | ||
than as a string literal. This allows for using the fully qualified name of the | ||
HIGHLIGHTING_ID | ||
* Tag[@alternatives] the value of RegisterConfigurableSeverityAttribute.AlternativeIDs | ||
* Tag/Title is the short title of the severity, usually matching the highlight's MESSAGE | ||
* Tag/Description is a longer description, shown in the options pages | ||
* Tag/CompoundItemName only used if Tag[@type] is missing or "simple". Sets the compound | ||
item name | ||
--> | ||
<SeverityConfiguration> | ||
<Group name="AzureHighlightingGroupIds.FunctionApp"> | ||
|
||
<Tag externalName="TimerTriggerCronExpressionError.HIGHLIGHTING_ID" default="ERROR"> | ||
<Title>Invalid Function App Timer Trigger Cron expression</Title> | ||
<Description>Function App Timer Trigger Cron expresion is not valid and can not be used.</Description> | ||
</Tag> | ||
|
||
</Group> | ||
</SeverityConfiguration> | ||
|
||
<!-- Warning, Error, Info or LocalAndGlobal | ||
* @name - name of the highlighting. Suffix based on parent element is automatically added | ||
* @configurableSeverity - the value of HIGHLIGHTING_ID. Treated as a string literal and quoted | ||
* @externalConfigurableSeverity - the value of HIGHLIGHTING_ID. Treated verbatim to refer to code | ||
* @staticGroup - define the static group this highlight belongs to. Must have been registered | ||
in /Errors/StaticSeverityGroups | ||
* ./Parameter - multiple elements for constructor parameters. Requires the @name and @type attributes. | ||
* ./Parameter[@name] is capitalised and turned into a public property | ||
* ./Message[@value] the string format template for the message | ||
* ./Message/Argument multiple code snippets to be used as the arguments to string.Format | ||
* ./Range code snippt to be the return value of CalculateRange() | ||
* ./Behavour - NOTE THE SPELLING! | ||
* ./Behavour[@attributeID] (optional) defines ConfigurableSeverityHighlightingAttribute.AttributeId | ||
* ./Behavour[@overlapResolvePolicy] (optional) defines ConfigurableSeverityHighlightingAttribute.OverlapResolve | ||
* ./Behavour/QuickFix multiple elements listing the types that will implement a quick fix | ||
for this highlight. Only used when the file is processed in QUICKFIX mode (default is | ||
to use ERROR mode) | ||
--> | ||
<Error staticGroup="AzureErrors" | ||
name="TimerTriggerCronExpression" | ||
configurableSeverity="Azure.FSharp.FunctionApp.TimerTriggerCronExpression"> | ||
|
||
<Parameter type="IExpression" name="expression" /> | ||
<Parameter type="string" name="cronErrorMessage"/> | ||
<Message value="{0}"> | ||
<Argument>cronErrorMessage</Argument> | ||
</Message> | ||
<Range>Expression.GetHighlightingRange()</Range> | ||
<Behavour overlapResolvePolicy="NONE"/> | ||
</Error> | ||
|
||
</Errors> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Copyright (c) 2020 JetBrains s.r.o. | ||
// <p/> | ||
// All rights reserved. | ||
// <p/> | ||
// MIT License | ||
// <p/> | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation | ||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and | ||
// to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
// <p/> | ||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of | ||
// the Software. | ||
// <p/> | ||
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO | ||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
|
||
using JetBrains.Annotations; | ||
using JetBrains.DocumentModel; | ||
using JetBrains.ReSharper.Azure.Psi.FunctionApp; | ||
using JetBrains.ReSharper.Feature.Services.CodeCompletion; | ||
using JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure; | ||
using JetBrains.ReSharper.Feature.Services.CodeCompletion.Infrastructure.LookupItems; | ||
using JetBrains.ReSharper.Features.Intellisense.CodeCompletion.CSharp; | ||
using JetBrains.ReSharper.Plugins.FSharp.Psi; | ||
using JetBrains.ReSharper.Plugins.FSharp.Psi.Features.CodeCompletion; | ||
using JetBrains.ReSharper.Plugins.FSharp.Psi.Impl; | ||
using JetBrains.ReSharper.Plugins.FSharp.Psi.Tree; | ||
using JetBrains.ReSharper.Psi; | ||
using JetBrains.ReSharper.Psi.Tree; | ||
|
||
namespace JetBrains.ReSharper.Azure.FSharp.FunctionApp.CodeCompletion.Rules | ||
{ | ||
[Language(typeof(FSharpLanguage))] | ||
public class FSharpTimerTriggerCronArgumentsProvider : ItemsProviderOfSpecificContext<FSharpCodeCompletionContext> | ||
{ | ||
// TODO: Refactor so C# and F# share this | ||
private readonly struct CronSuggestion | ||
{ | ||
public readonly string Expression; | ||
public readonly string Description; | ||
|
||
public CronSuggestion(string expression, string description) | ||
{ | ||
Expression = expression; | ||
Description = description; | ||
} | ||
} | ||
|
||
// TODO: Refactor so C# and F# share this | ||
private readonly CronSuggestion[] CronSuggestions = | ||
{ | ||
new CronSuggestion("* * * * * *", "Every second"), | ||
new CronSuggestion("0 * * * * *", "Every minute"), | ||
new CronSuggestion("0 */5 * * * *", "Every 5 minutes"), | ||
new CronSuggestion("0 0 * * * *", "Every hour"), | ||
new CronSuggestion("0 0 */6 * * *", "Every 6 hours at minute 0"), | ||
new CronSuggestion("0 0 8-18 * * *", "Every hour between 08:00 AM and 06:59 PM"), | ||
new CronSuggestion("0 0 0 * * *", "At 12:00 AM"), | ||
new CronSuggestion("0 0 10 * * *", "At 10:00 AM"), | ||
new CronSuggestion("0 0 * * * 1-5", "Every hour, Monday through Friday"), | ||
new CronSuggestion("0 0 0 * * 0", "At 12:00 AM, only on Sunday"), | ||
new CronSuggestion("0 0 9 * * Mon", "At 09:00 AM, only on Monday"), | ||
new CronSuggestion("0 0 0 1 * *", "At 12:00 AM, on day 1 of the month"), | ||
new CronSuggestion("0 0 0 1 1 *", "At 12:00 AM, on day 1 of the month, only in January"), | ||
new CronSuggestion("0 0 * * * Sun", "Every hour, only on Sunday"), | ||
new CronSuggestion("0 0 0 * * Sat,Sun", "At 12:00 AM, only on Saturday and Sunday"), | ||
new CronSuggestion("0 0 0 * * 6,0", "At 12:00 AM, only on Saturday and Sunday"), | ||
new CronSuggestion("0 0 0 1-7 * Sun", "At 12:00 AM, between day 1 and 7 of the month, only on Sunday"), | ||
new CronSuggestion("11 5 23 * * *", "At 11:05:11 PM"), | ||
new CronSuggestion("*/15 * * * * *", "Every 15 seconds"), | ||
new CronSuggestion("0 30 9 * Jan Mon", "At 09:30 AM, only on Monday, only in January") | ||
}; | ||
|
||
protected override bool IsAvailable(FSharpCodeCompletionContext context) | ||
{ | ||
return IsStringLiteral(context) && | ||
IsTimerTriggerAnnotation(context) && | ||
context.BasicContext.CodeCompletionType == CodeCompletionType.BasicCompletion; | ||
} | ||
|
||
protected override bool AddLookupItems(FSharpCodeCompletionContext context, IItemsCollector collector) | ||
{ | ||
foreach (var cronSuggestion in CronSuggestions) | ||
{ | ||
var token = context.TokenAtCaret as ITokenNode; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @auduchinok Seems completion can only be triggered right before a string, so (I could be misunderstanding the PSI as well) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may have weird completion contexts in places where it wasn't previously possible to complete things. There're chances I'll need to fix it up a bit for completion inside strings to work as expected. |
||
if (token == null) return false; | ||
|
||
var literalExpression = token.Parent as ILiteralExpr; | ||
if (literalExpression == null) return false; | ||
|
||
var ranges = GetRangeFor(literalExpression); | ||
|
||
var lookupItem = | ||
CSharpLookupItemFactory.Instance.CreateTextLookupItem(new TextLookupRanges(ranges, ranges), | ||
"\"" + cronSuggestion.Expression + "\""); | ||
|
||
lookupItem.Presentation.DisplayName.Text = cronSuggestion.Expression; | ||
lookupItem.Presentation.DisplayTypeName.Text = cronSuggestion.Description; | ||
|
||
// Replace spaces prefixes with nbsp to make sure <space> terminates the lookup. | ||
// It is required since we need to support case when a user choose to complete by <space>. | ||
// TODO: Disable until RIDER-45943 is fixed. | ||
// lookupItem.WithMatcher(item => | ||
// new TextualMatcher<TextualInfo>(item.Info.Text.Replace(" ", "·"), item.Info)); | ||
|
||
collector.Add(lookupItem); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private static DocumentRange GetRangeFor([NotNull] ILiteralExpr expression) | ||
{ | ||
var underQuotesRange = expression.GetTreeTextRange(); | ||
var containingFile = expression.GetContainingFile(); | ||
return containingFile?.GetDocumentRange(underQuotesRange) ?? DocumentRange.InvalidRange; | ||
} | ||
|
||
private bool IsStringLiteral([NotNull] FSharpCodeCompletionContext context) | ||
{ | ||
return context.TokenAtCaret is ITokenNode token && | ||
token.GetTokenType().IsStringLiteral; | ||
} | ||
|
||
private bool IsTimerTriggerAnnotation([NotNull] FSharpCodeCompletionContext context) | ||
{ | ||
var token = context.TokenAtCaret as ITokenNode; | ||
if (token == null) return false; | ||
|
||
var literalExpression = token.Parent as ILiteralExpr; | ||
var attribute = AttributeNavigator.GetByExpression(literalExpression.IgnoreParentParens()); | ||
|
||
if (attribute == null) return false; | ||
if (attribute.Arguments.Count != 1) return false; | ||
|
||
var resolveResult = attribute.ReferenceName.Reference.Resolve(); | ||
return resolveResult.DeclaredElement is ITypeElement typeElement && | ||
FunctionAppFinder.IsTimerTriggerAttribute(typeElement); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mfilippov / @auduchinok Right now, I have referenced assemblies from the F# plugin manually. Support for "full plugins" (back-end and front-end) seem hard to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's true. We don't have auto-reference for bundled plugins :( Maybe we should think about it.