Initial end to end working completion with tests
This commit is contained in:
Родитель
f819973720
Коммит
dc849d3adf
12
README.md
12
README.md
|
@ -1,2 +1,12 @@
|
||||||
# codeanalysis
|
# Xamarin CodeAnalyis
|
||||||
|
|
||||||
Analyzers, code fixers and custom completion for Xamarin projects
|
Analyzers, code fixers and custom completion for Xamarin projects
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Just open Xamarin.CodeAnalysis.sln and build.
|
||||||
|
|
||||||
|
> NOTE: the *first* build ever needs to be run from an administrator command prompt,
|
||||||
|
> because the extension provides MSBuild targets that need to be symlinked from the
|
||||||
|
> `Exp` hive location to the `VsInstallDir\MSBuild` location, which requires elevation.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.28218.60
|
VisualStudioVersion = 16.0.28223.52
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.CodeAnalysis", "src\Xamarin.CodeAnalysis\Xamarin.CodeAnalysis.csproj", "{A83DFC4B-4270-4A1A-8438-897A8AA61AE0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.CodeAnalysis", "src\Xamarin.CodeAnalysis\Xamarin.CodeAnalysis.csproj", "{A83DFC4B-4270-4A1A-8438-897A8AA61AE0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
src\GitInfo.txt = src\GitInfo.txt
|
src\GitInfo.txt = src\GitInfo.txt
|
||||||
src\NuGet.Config = src\NuGet.Config
|
src\NuGet.Config = src\NuGet.Config
|
||||||
Packages.props = Packages.props
|
Packages.props = Packages.props
|
||||||
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GitSkipCache Condition="'$(CI)' == 'true'">true</GitSkipCache>
|
<GitSkipCache Condition="$(CI)">true</GitSkipCache>
|
||||||
<Configuration Condition="'$(Configuration)' == '' and '$(CI)' == 'true'">Release</Configuration>
|
<Configuration Condition="'$(Configuration)' == '' and $(CI)">Release</Configuration>
|
||||||
|
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Redeclared by GitInfo -->
|
<!-- Redeclared by GitInfo -->
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
|
||||||
|
namespace Xamarin.CodeAnalysis
|
||||||
|
{
|
||||||
|
internal static class Extensions
|
||||||
|
{
|
||||||
|
//public static async Task<SemanticModel> GetSemanticModelForSpanAsync(this Document document, TextSpan span, CancellationToken cancellationToken)
|
||||||
|
//{
|
||||||
|
// var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
// var token = root.FindToken(span.Start);
|
||||||
|
// if (token.Parent == null)
|
||||||
|
// {
|
||||||
|
// return await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Completion;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Options;
|
||||||
|
using Microsoft.CodeAnalysis.Tags;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
|
||||||
|
namespace Xamarin.CodeAnalysis
|
||||||
|
{
|
||||||
|
[ExportCompletionProvider(nameof(ResourceCompletionProvider), LanguageNames.CSharp)]
|
||||||
|
public class ResourceCompletionProvider : CompletionProvider
|
||||||
|
{
|
||||||
|
private static readonly CompletionItemRules StandardCompletionRules = CompletionItemRules.Default.WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection);
|
||||||
|
|
||||||
|
public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, OptionSet options)
|
||||||
|
{
|
||||||
|
// TODO: should trigger if we're inside a string
|
||||||
|
return base.ShouldTriggerCompletion(text, caretPosition, trigger, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// TODO: get the actual xml file location.
|
||||||
|
return Task.FromResult(CompletionDescription.FromText($"{item.Properties["Path"]}({item.Properties["Line"]},{item.Properties["Position"]})"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<CompletionChange> GetChangeAsync(Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// Determine change to insert
|
||||||
|
return base.GetChangeAsync(document, item, commitKey, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly Regex isResourceValue = new Regex(@"Resources\\values\\.*.xml", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public override async Task ProvideCompletionsAsync(CompletionContext completionContext)
|
||||||
|
{
|
||||||
|
if (!completionContext.Document.SupportsSemanticModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var position = completionContext.Position;
|
||||||
|
var document = completionContext.Document;
|
||||||
|
var cancellationToken = completionContext.CancellationToken;
|
||||||
|
|
||||||
|
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
var span = completionContext.CompletionListSpan;
|
||||||
|
var token = root.FindToken(span.Start);
|
||||||
|
var node = token.Parent?.AncestorsAndSelf().FirstOrDefault(a => a.FullSpan.Contains(span));
|
||||||
|
if (node is LiteralExpressionSyntax literal &&
|
||||||
|
node?.Parent is AttributeArgumentSyntax argument &&
|
||||||
|
node?.Parent?.Parent?.Parent is AttributeSyntax attribute)
|
||||||
|
{
|
||||||
|
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
|
||||||
|
var symbol = semanticModel.GetSymbolInfo(attribute, cancellationToken).Symbol;
|
||||||
|
if (symbol?.ContainingType.ToDisplayString() == "Android.App.ActivityAttribute" ||
|
||||||
|
(symbol == null && attribute.Name.ToString() == "Activity"))
|
||||||
|
{
|
||||||
|
var name = argument.NameEquals.Name.ToString();
|
||||||
|
// TODO: consider resource files some other way?
|
||||||
|
var valueDocs = document.Project.AdditionalDocuments.Where(doc => isResourceValue.IsMatch(doc.FilePath));
|
||||||
|
var elementName = "string";
|
||||||
|
if (name == "Theme")
|
||||||
|
elementName = "style";
|
||||||
|
|
||||||
|
var strings = new HashSet<string>();
|
||||||
|
var styles = new HashSet<string>();
|
||||||
|
var xmlSettings = new XmlReaderSettings
|
||||||
|
{
|
||||||
|
IgnoreComments = true,
|
||||||
|
IgnoreProcessingInstructions = true,
|
||||||
|
IgnoreWhitespace = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var doc in valueDocs)
|
||||||
|
{
|
||||||
|
XmlReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(doc.FilePath))
|
||||||
|
{
|
||||||
|
reader = XmlReader.Create(doc.FilePath, xmlSettings);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In tests, the file doesn't exist.
|
||||||
|
var writer = new StringWriter();
|
||||||
|
(await doc.GetTextAsync(cancellationToken)).Write(writer, cancellationToken);
|
||||||
|
reader = XmlReader.Create(new StringReader(writer.ToString()), xmlSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.MoveToContent() == XmlNodeType.Element)
|
||||||
|
{
|
||||||
|
// TODO: cache already parsed results as long as the text version remains the same?
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.LocalName == elementName && reader.GetAttribute("name") is string id)
|
||||||
|
{
|
||||||
|
completionContext.AddItem(CompletionItem.Create($"@{reader.LocalName}/{id}",
|
||||||
|
tags: ImmutableArray.Create(WellKnownTags.Constant, "Xamarin"),
|
||||||
|
properties: ImmutableDictionary.Create<string, string>()
|
||||||
|
.Add("Path", doc.FilePath)
|
||||||
|
.Add("Line", ((IXmlLineInfo)reader).LineNumber.ToString())
|
||||||
|
.Add("Position", ((IXmlLineInfo)reader).LinePosition.ToString()),
|
||||||
|
rules: StandardCompletionRules));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
reader?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.Completion;
|
|
||||||
using Microsoft.CodeAnalysis.Options;
|
|
||||||
using Microsoft.CodeAnalysis.Tags;
|
|
||||||
using Microsoft.CodeAnalysis.Text;
|
|
||||||
|
|
||||||
namespace Xamarin.CodeAnalysis
|
|
||||||
{
|
|
||||||
[ExportCompletionProvider(nameof(StringCompletionProvider), LanguageNames.CSharp)]
|
|
||||||
public class StringCompletionProvider : CompletionProvider
|
|
||||||
{
|
|
||||||
private static readonly CompletionItemRules StandardCompletionRules = CompletionItemRules.Default.WithSelectionBehavior(CompletionItemSelectionBehavior.SoftSelection);
|
|
||||||
|
|
||||||
public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, OptionSet options)
|
|
||||||
{
|
|
||||||
// TODO: should trigger if we're inside a string
|
|
||||||
return base.ShouldTriggerCompletion(text, caretPosition, trigger, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// TODO: get the actual xml file location.
|
|
||||||
return Task.FromResult(CompletionDescription.FromText("strings.xml(120,34)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<CompletionChange> GetChangeAsync(Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// Determine change to insert
|
|
||||||
return base.GetChangeAsync(document, item, commitKey, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ProvideCompletionsAsync(CompletionContext context)
|
|
||||||
{
|
|
||||||
context.AddItem(CompletionItem.Create("@style/MainTheme",
|
|
||||||
tags: ImmutableArray.Create(WellKnownTags.Constant),
|
|
||||||
// TODO: props should contain the file location it was retrieved from to use in description
|
|
||||||
// properties: ImmutableDictionary.CreateBuilder<string, string>().Add(,
|
|
||||||
rules: StandardCompletionRules));
|
|
||||||
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="@(AndroidResource -> WithMetadataValue('RelativeDir', 'Resources\values\'))" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -20,12 +20,15 @@
|
||||||
<IsExperimental>true</IsExperimental>
|
<IsExperimental>true</IsExperimental>
|
||||||
<IsSystemComponent>false</IsSystemComponent>
|
<IsSystemComponent>false</IsSystemComponent>
|
||||||
|
|
||||||
|
<!--<CreateVsixContainer>true</CreateVsixContainer>-->
|
||||||
<VsixVersion>42.42.42</VsixVersion>
|
<VsixVersion>42.42.42</VsixVersion>
|
||||||
|
<DefaultItemExcludes>*.targets</DefaultItemExcludes>
|
||||||
|
|
||||||
<RestoreSources>$(RestoreSources);https://api.nuget.org/v3/index.json;https://nugets.blob.core.windows.net/xvssdk/index.json</RestoreSources>
|
<RestoreSources>$(RestoreSources);https://api.nuget.org/v3/index.json;https://nugets.blob.core.windows.net/xvssdk/index.json</RestoreSources>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(CI)' == 'true'">
|
|
||||||
|
<PropertyGroup Condition="$(CI)">
|
||||||
<IsProductComponent>true</IsProductComponent>
|
<IsProductComponent>true</IsProductComponent>
|
||||||
<IsExperimental>false</IsExperimental>
|
<IsExperimental>false</IsExperimental>
|
||||||
<IsSystemComponent>true</IsSystemComponent>
|
<IsSystemComponent>true</IsSystemComponent>
|
||||||
|
@ -59,9 +62,16 @@
|
||||||
<Resource Include="Properties\ICN_Xamarin.ico" />
|
<Resource Include="Properties\ICN_Xamarin.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="*.targets" IncludeInVSIX="true" VSIXSubPath="Xamarin" SymLink="true" />
|
||||||
|
<Content Update="Xamarin.CodeAnalysis.ImportAfter.targets" VSIXSubPath="Current\Microsoft.Common.Targets\ImportAfter" />
|
||||||
|
<Content Update="@(Content)" Condition="$(CI)" InstallRoot="MSBuild" />
|
||||||
|
<UpToDateCheckInput Include="@(Content)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="GetVsixVersion" DependsOnTargets="SetVersions" Returns="$(VsixVersion)">
|
<Target Name="GetVsixVersion" DependsOnTargets="SetVersions" Returns="$(VsixVersion)">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VsixVersion Condition="'$(CI)' == 'true'">$(GitSemVerMajor).$(GitSemVerMinor).$(GitSemVerPatch)</VsixVersion>
|
<VsixVersion Condition="$(CI)">$(GitSemVerMajor).$(GitSemVerMinor).$(GitSemVerPatch)</VsixVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
@ -83,4 +93,71 @@
|
||||||
<Target Name="IsExperimental" Returns="$(IsExperimental)" />
|
<Target Name="IsExperimental" Returns="$(IsExperimental)" />
|
||||||
<Target Name="IsSystemComponent" Returns="$(IsSystemComponent)" />
|
<Target Name="IsSystemComponent" Returns="$(IsSystemComponent)" />
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||||
|
<BuildDependsOn Condition="!$(CI)">
|
||||||
|
$(BuildDependsOn);
|
||||||
|
SymLink
|
||||||
|
</BuildDependsOn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="SymLink" DependsOnTargets="IsAdministrator;CollectLinkItems;ReplaceLinkItems" />
|
||||||
|
|
||||||
|
<Target Name="IsAdministrator">
|
||||||
|
<IsAdministrator>
|
||||||
|
<Output TaskParameter="Result" PropertyName="IsAdministrator" />
|
||||||
|
</IsAdministrator>
|
||||||
|
<Warning Text="Current user isn't an Administrator, so MSBuild artifacts won't be symlinked." Condition="'$(IsAdministrator)' == 'false'" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<MkLinkCandidate>
|
||||||
|
<Exists>false</Exists>
|
||||||
|
<IsSymLink>false</IsSymLink>
|
||||||
|
</MkLinkCandidate>
|
||||||
|
<VSIXSourceItem>
|
||||||
|
<VSIXSubPath />
|
||||||
|
<SymLink />
|
||||||
|
</VSIXSourceItem>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
|
<Target Name="CollectLinkItems" DependsOnTargets="IsAdministrator;GetVsixDeploymentPath;GetVsixSourceItems" Condition="'$(IsAdministrator)' == 'true'">
|
||||||
|
<ItemGroup>
|
||||||
|
<MkLinkCandidate Include="@(VSIXSourceItem -> '$(MSBuildExtensionsPath)\%(VSIXSubPath)\%(Filename)%(Extension)')" Condition="'%(SymLink)' == 'true'">
|
||||||
|
<LinkTarget>$(VsixDeploymentPath)\%(VSIXSubPath)\%(Filename)%(Extension)</LinkTarget>
|
||||||
|
</MkLinkCandidate>
|
||||||
|
<MkLinkCandidate Condition="Exists('%(FullPath)')">
|
||||||
|
<IsSymLink Condition="$([MSBuild]::BitwiseAnd(1024, $([System.IO.File]::GetAttributes('%(FullPath)')))) == '1024'">true</IsSymLink>
|
||||||
|
<Exists>true</Exists>
|
||||||
|
</MkLinkCandidate>
|
||||||
|
<MkLinkSource Include="@(MkLinkCandidate)" Condition="!Exists('%(FullPath)') Or '%(IsSymLink)' == 'false'" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="ReplaceLinkItems" Condition="'@(MkLinkSource)' != '' And '$(IsAdministrator)' == 'true'">
|
||||||
|
<Message Text="In $(Configuration) builds, we attempt to symlink MSBuild files with current project output." Importance="high" />
|
||||||
|
<ItemGroup>
|
||||||
|
<_FilesToDelete Include="@(MkLinkSource -> WithMetadataValue('Exists', 'true'))" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Exec Command='del "%(_FilesToDelete.FullPath)"' EchoOff="true" Condition="'@(_FilesToDelete)' != ''" />
|
||||||
|
<Exec Command='mklink "%(MkLinkSource.Identity)" "%(MkLinkSource.LinkTarget)"'
|
||||||
|
ConsoleToMSBuild="true"
|
||||||
|
EchoOff="true"
|
||||||
|
Condition="Exists('%(MkLinkSource.RootDir)%(MkLinkSource.Directory)')">
|
||||||
|
<Output TaskParameter="ConsoleOutput" ItemName="MkLinked" />
|
||||||
|
</Exec>
|
||||||
|
<Message Importance="high" Text="%(MkLinked.Identity)" Condition="'@(MkLinked)' != ''" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<UsingTask TaskName="IsAdministrator" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
||||||
|
<ParameterGroup>
|
||||||
|
<Result ParameterType="System.Boolean" Output="true" />
|
||||||
|
</ParameterGroup>
|
||||||
|
<Task>
|
||||||
|
<Using Namespace="System.Security.Principal" />
|
||||||
|
<Code Type="Fragment" Language="cs">
|
||||||
|
Result = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
</Code>
|
||||||
|
</Task>
|
||||||
|
</UsingTask>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||||
|
|
||||||
|
<XamarinCodeAnalysisTargets Condition="'$(XamarinCodeAnalysisTargets)' == ''">$(MSBuildExtensionsPath)\Xamarin\Xamarin.CodeAnalysis.targets</XamarinCodeAnalysisTargets>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Condition="Exists('$(XamarinCodeAnalysisTargets)')" Project="$(XamarinCodeAnalysisTargets)" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
|
||||||
|
<Import Project="Xamarin.CodeAnalysis.Android.targets" Condition="'$(TargetFrameworkIdentifier)' == 'MonoAndroid'" />
|
||||||
|
|
||||||
|
</Project>
|
|
@ -1,6 +1,4 @@
|
||||||
|
using System.Linq;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Completion;
|
using Microsoft.CodeAnalysis.Completion;
|
||||||
|
@ -13,21 +11,24 @@ namespace Xamarin.CodeAnalysis.Tests
|
||||||
public class CompletionTests
|
public class CompletionTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(@"using System;
|
[InlineData(@"using Android.App;
|
||||||
public class Foo
|
using Android.Support.Design.Widget;
|
||||||
|
using Android.Support.V7.App;
|
||||||
|
using Android.Views;
|
||||||
|
|
||||||
|
[Activity(Label = ""`"", MainLauncher = true)]
|
||||||
|
public class MainActivity : AppCompatActivity, NavigationView.IOnNavigationItemSelectedListener
|
||||||
{
|
{
|
||||||
public void Do()
|
public bool OnNavigationItemSelected(IMenuItem menuItem) => true;
|
||||||
{
|
}
|
||||||
Console.WriteLine(""`"");
|
", "@string/app_name")]
|
||||||
}
|
public async Task can_retrieve_completion(string code, string completion)
|
||||||
}")]
|
|
||||||
public async Task can_retrieve_completion(string code)
|
|
||||||
{
|
{
|
||||||
var hostServices = MefHostServices.Create(MefHostServices.DefaultAssemblies.Concat(
|
var hostServices = MefHostServices.Create(MefHostServices.DefaultAssemblies.Concat(
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
typeof(CompletionService).Assembly,
|
typeof(CompletionService).Assembly,
|
||||||
typeof(StringCompletionProvider).Assembly,
|
typeof(ResourceCompletionProvider).Assembly,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var workspace = new AdhocWorkspace(hostServices);
|
var workspace = new AdhocWorkspace(hostServices);
|
||||||
|
@ -40,7 +41,17 @@ public class Foo
|
||||||
MetadataReference.CreateFromFile("Xamarin.CodeAnalysis.dll"),
|
MetadataReference.CreateFromFile("Xamarin.CodeAnalysis.dll"),
|
||||||
MetadataReference.CreateFromFile("Xamarin.CodeAnalysis.Completion.dll"),
|
MetadataReference.CreateFromFile("Xamarin.CodeAnalysis.Completion.dll"),
|
||||||
})
|
})
|
||||||
.AddDocument("TestDocument.cs", code);
|
.AddAdditionalDocument("strings.xml", @"<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<resources>
|
||||||
|
<string name='app_name'>TestApp</string>
|
||||||
|
</resources>", new[] { "Resources\\values" }, "Resources\\values\\strings.xml")
|
||||||
|
.Project
|
||||||
|
.AddAdditionalDocument("styles.xml", @"<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<resources>
|
||||||
|
<style name='AppTheme' parent='Theme.AppCompat.Light.DarkActionBar' />
|
||||||
|
</resources>", new[] { "Resources\\values" }, "Resources\\values\\styles.xml")
|
||||||
|
.Project
|
||||||
|
.AddDocument("TestDocument.cs", code.Replace("`", ""));
|
||||||
|
|
||||||
var service = CompletionService.GetService(document);
|
var service = CompletionService.GetService(document);
|
||||||
Assert.NotNull(service);
|
Assert.NotNull(service);
|
||||||
|
@ -51,7 +62,50 @@ public class Foo
|
||||||
var completions = await service.GetCompletionsAsync(document, caret);
|
var completions = await service.GetCompletionsAsync(document, caret);
|
||||||
|
|
||||||
Assert.NotNull(completions);
|
Assert.NotNull(completions);
|
||||||
Assert.NotEmpty(completions.Items);
|
Assert.Contains(completions.Items, x => x.Tags.Contains("Xamarin"));
|
||||||
|
Assert.Contains(completions.Items, x => x.DisplayText == completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(@"using System;
|
||||||
|
public class Foo
|
||||||
|
{
|
||||||
|
public void Do()
|
||||||
|
{
|
||||||
|
Console.`WriteLine("""");
|
||||||
|
}
|
||||||
|
}")]
|
||||||
|
public async Task does_not_trigger_completion(string code)
|
||||||
|
{
|
||||||
|
var hostServices = MefHostServices.Create(MefHostServices.DefaultAssemblies.Concat(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
typeof(CompletionService).Assembly,
|
||||||
|
typeof(ResourceCompletionProvider).Assembly,
|
||||||
|
}));
|
||||||
|
|
||||||
|
var workspace = new AdhocWorkspace(hostServices);
|
||||||
|
var document = workspace
|
||||||
|
.AddProject("TestProject", LanguageNames.CSharp)
|
||||||
|
.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
|
||||||
|
.WithMetadataReferences(new MetadataReference[]
|
||||||
|
{
|
||||||
|
MetadataReference.CreateFromFile(ThisAssembly.Metadata.NETStandardReference),
|
||||||
|
MetadataReference.CreateFromFile("Xamarin.CodeAnalysis.dll"),
|
||||||
|
MetadataReference.CreateFromFile("Xamarin.CodeAnalysis.Completion.dll"),
|
||||||
|
})
|
||||||
|
.AddDocument("TestDocument.cs", code.Replace("`", ""));
|
||||||
|
|
||||||
|
var service = CompletionService.GetService(document);
|
||||||
|
Assert.NotNull(service);
|
||||||
|
|
||||||
|
var caret = code.IndexOf('`');
|
||||||
|
Assert.NotEqual(-1, caret);
|
||||||
|
|
||||||
|
var completions = await service.GetCompletionsAsync(document, caret);
|
||||||
|
|
||||||
|
Assert.NotNull(completions);
|
||||||
|
Assert.DoesNotContain(completions.Items, x => x.Tags.Contains("Xamarin"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net46</TargetFramework>
|
<TargetFramework>net46</TargetFramework>
|
||||||
|
<TargetFramework Condition="'$(VisualStudioVersion)' == '16.0'">net472</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче