<commit_before>﻿namespace FSharpVSPowerTools.Linting

open System
open Microsoft.VisualStudio.Text
open Microsoft.VisualStudio.Text.Tagging
open Microsoft.VisualStudio.Shell.Interop
open FSharpVSPowerTools
open FSharpVSPowerTools.AsyncMaybe
open FSharpVSPowerTools.ProjectSystem
open FSharpLint.Application
open Microsoft.FSharp.Compiler
open System.Threading

type LintTag(tooltip) = 
    inherit ErrorTag(Constants.LintTagErrorType, tooltip)

type LintTagger(textDocument: ITextDocument,
                vsLanguageService: VSLanguageService, 
                serviceProvider: IServiceProvider,
                projectFactory: ProjectFactory) as self =
    let tagsChanged = Event<_, _>()
    let mutable wordSpans = []
    let buffer = textDocument.TextBuffer

    let updateAtCaretPosition () =
        let uiContext = SynchronizationContext.Current
        asyncMaybe {
            let dte = serviceProvider.GetService<EnvDTE.DTE, SDTE>()
            let! doc = dte.GetCurrentDocument(textDocument.FilePath)
            let! project = projectFactory.CreateForDocument buffer doc
            let source = buffer.CurrentSnapshot.GetText()
            let! parseFileResults = vsLanguageService.ParseFileInProject (doc.FullName, source, project) |> liftAsync
            let! ast = parseFileResults.ParseTree
            let res = 
                Lint.lintParsedFile
                    { FinishEarly = None
                      Configuration = None
                      ReceivedWarning = None }
                    { Ast = ast
                      Source = source
                      TypeCheckResults = None }
                    doc.FullName

            return
                match res with
                | LintResult.Success warnings ->
                    warnings 
                    //|> Seq.distinctBy (fun warn -> warn.Range, warn.Info)
                    |> Seq.choose (fun warn ->
                        let r = warn.Range
                        let endCol =
                            if r.StartLine = r.EndLine then
                                min r.EndColumn (r.StartColumn + 2)
                            else r.StartColumn + 2
                        let range =
                            Range.mkRange "" 
                                (Range.mkPos r.StartLine r.StartColumn)
                                (Range.mkPos r.StartLine endCol)

                        fromFSharpRange buffer.CurrentSnapshot range
                        |> Option.map (fun span -> warn, span))
                    |> Seq.toList
                | LintResult.Failure _ -> []
        }
        |> Async.bind (fun spans -> 
            async {
                let spans = spans |> Option.getOrElse []
                wordSpans <- spans<commit_msg>Initial loading of config for linter<commit_after>﻿namespace FSharpVSPowerTools.Linting

open System
open Microsoft.VisualStudio.Text
open Microsoft.VisualStudio.Text.Tagging
open Microsoft.VisualStudio.Shell.Interop
open FSharpVSPowerTools
open FSharpVSPowerTools.AsyncMaybe
open FSharpVSPowerTools.ProjectSystem
open FSharpLint.Application
open Microsoft.FSharp.Compiler
open System.Threading

type LintTag(tooltip) = 
    inherit ErrorTag(Constants.LintTagErrorType, tooltip)

type LintTagger(textDocument: ITextDocument,
                vsLanguageService: VSLanguageService, 
                serviceProvider: IServiceProvider,
                projectFactory: ProjectFactory) as self =
    let tagsChanged = Event<_, _>()
    let mutable wordSpans = []
    let buffer = textDocument.TextBuffer

    let updateAtCaretPosition () =
        let uiContext = SynchronizationContext.Current
        asyncMaybe {
            let dte = serviceProvider.GetService<EnvDTE.DTE, SDTE>()
            let! doc = dte.GetCurrentDocument(textDocument.FilePath)
            let! project = projectFactory.CreateForDocument buffer doc
            let source = buffer.CurrentSnapshot.GetText()
            let! parseFileResults = vsLanguageService.ParseFileInProject (doc.FullName, source, project) |> liftAsync
            let! ast = parseFileResults.ParseTree

            let config = 
                match ConfigurationManagement.loadConfigurationForProject project.ProjectFileName with
                | ConfigurationManagement.ConfigurationResult.Success(config) -> Some(config)
                | ConfigurationManagement.ConfigurationResult.Failure(failure) -> 
                    let failureMessage =
                        match failure with
                        | ConfigurationManagement.ConfigFailure.FailedToLoadConfig(message) -> message
                        | ConfigurationManagement.ConfigFailure.RunTimeConfigError -> 
                            "Failed when checking config at run time."

                    Logging.logWarning 
                        "Failed to load config for project %s. Failed because: %s" 
                        project.ProjectFileName
                        failureMessage

                    None

            let res = 
                Lint.lintParsedFile
                    { Lint.OptionalLintParameters.Default with Configuration = config }
                    { Ast = ast
                      Source = source
                      TypeCheckResults = None }
                    doc.FullName

            return
                match res with
                | LintResult.Success warnings ->
                    warnings 
                    |> Seq.choose (fun warn ->
                        let r = warn.Range
                        let endCol =
                            if r.StartLine = r.EndLine then
                                min r.EndColumn (r.StartColumn + 2)
                            else r.StartColumn + 2
                        let range =
                            Range.mkRange "" 
                                (Range.mkPos r.StartLine r.StartColumn)
                                (Range.mkPos r.StartLine endCol)

                        fromFSharpRange buffer.CurrentSnapshot range
                        |> Option.map (fun span -> warn, span))
                    |> Seq.toList
                | LintResult.Failure _ -> []
        }
        |> Async.bind (fun spans -> 
            async {
                let spans = spans |> Option.getOrElse []
                wordSpans <- spans
{'fraction_non_alphanumeric': 0.06310295745690336, 'fraction_numerical': 0.0006326111023248458, 'mean_word_length': 1.503562945368171, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1777399', 'n_tokens_mistral': 1547, 'n_tokens_neox': 1408, 'n_words': 343}