|
| 1 | +module Language.LSP.CodeAction.GenerateDefNext |
| 2 | + |
| 3 | +import Core.Context |
| 4 | +import Core.Core |
| 5 | +import Core.Env |
| 6 | +import Core.Metadata |
| 7 | +import Core.UnifyState |
| 8 | +import Data.String |
| 9 | +import Idris.REPL.Opts |
| 10 | +import Idris.Resugar |
| 11 | +import Idris.Syntax |
| 12 | +import Language.JSON |
| 13 | +import Language.LSP.CodeAction |
| 14 | +import Language.LSP.CodeAction.Utils |
| 15 | +import Language.LSP.Message |
| 16 | +import Language.LSP.Utils |
| 17 | +import Parser.Unlit |
| 18 | +import Server.Configuration |
| 19 | +import Server.Log |
| 20 | +import Server.Utils |
| 21 | +import Libraries.Data.Tap |
| 22 | +import TTImp.Interactive.GenerateDef |
| 23 | +import TTImp.Interactive.ExprSearch |
| 24 | +import TTImp.TTImp |
| 25 | +import TTImp.TTImp.Functor |
| 26 | + |
| 27 | +generateDefNextKind : CodeActionKind |
| 28 | +generateDefNextKind = Other "refactor.rewrite.GenerateDefNext" |
| 29 | + |
| 30 | +isAllowed : CodeActionParams -> Bool |
| 31 | +isAllowed params = |
| 32 | + maybe True (\filter => (generateDefNextKind `elem` filter) || (RefactorRewrite `elem` filter)) params.context.only |
| 33 | + |
| 34 | +buildCodeAction : URI -> TextEdit -> CodeAction |
| 35 | +buildCodeAction uri edit = |
| 36 | + MkCodeAction |
| 37 | + { title = "Generate next definition" |
| 38 | + , kind = Just RefactorRewrite |
| 39 | + , diagnostics = Nothing |
| 40 | + , isPreferred = Nothing |
| 41 | + , disabled = Nothing |
| 42 | + , edit = Just $ MkWorkspaceEdit |
| 43 | + { changes = Just (singleton uri [edit]) |
| 44 | + , documentChanges = Nothing |
| 45 | + , changeAnnotations = Nothing |
| 46 | + } |
| 47 | + , command = Nothing |
| 48 | + , data_ = Nothing |
| 49 | + } |
| 50 | + |
| 51 | +-- first blank line going forward (in contrast to reversed implementation found in |
| 52 | +-- some other code actions. |
| 53 | +findBlankLine : List String -> Int -> Int |
| 54 | +findBlankLine [] acc = acc |
| 55 | +findBlankLine (x :: xs) acc = if trim x == "" then acc else findBlankLine xs (acc + 1) |
| 56 | + |
| 57 | +-- reproduced from compiler repo because it is not exported there (as of now) |
| 58 | +nextGenDef : {auto c : Ref Ctxt Defs} -> |
| 59 | + {auto u : Ref UST UState} -> |
| 60 | + {auto o : Ref ROpts REPLOpts} -> |
| 61 | + (reject : Nat) -> |
| 62 | + Core (Maybe (Int, (FC, List ImpClause))) |
| 63 | +nextGenDef reject |
| 64 | + = do opts <- get ROpts |
| 65 | + let Just (line, res) = gdResult opts |
| 66 | + | Nothing => pure Nothing |
| 67 | + Just (res, next) <- nextResult res |
| 68 | + | Nothing => |
| 69 | + do put ROpts ({ gdResult := Nothing } opts) |
| 70 | + pure Nothing |
| 71 | + put ROpts ({ gdResult := Just (line, next) } opts) |
| 72 | + case reject of |
| 73 | + Z => pure (Just (line, res)) |
| 74 | + S k => nextGenDef k |
| 75 | + |
| 76 | +export |
| 77 | +generateDefNext : Ref LSPConf LSPConfiguration |
| 78 | + => Ref MD Metadata |
| 79 | + => Ref Ctxt Defs |
| 80 | + => Ref UST UState |
| 81 | + => Ref Syn SyntaxInfo |
| 82 | + => Ref ROpts REPLOpts |
| 83 | + => CodeActionParams -> Core (List CodeAction) |
| 84 | +generateDefNext params = do |
| 85 | + let True = isAllowed params |
| 86 | + | False => logI GenerateDefNext "Skipped" >> pure [] |
| 87 | + logI GenerateDefNext "Checking for \{show params.textDocument.uri} at \{show params.range}" |
| 88 | + |
| 89 | + withSingleLine GenerateDefNext params (pure []) $ \line => do |
| 90 | + withMultipleCache GenerateDefNext params GenerateDefNext $ do |
| 91 | + defs <- branch |
| 92 | + Just (loc, n, _, _) <- findTyDeclAt (\p, n => onLine line p) |
| 93 | + | Nothing => logD GenerateDef "No name found at line \{show line}" >> pure [] |
| 94 | + logD CaseSplit "Found type declaration \{show n}" |
| 95 | + |
| 96 | + previousResults <- gdResult <$> get ROpts |
| 97 | + let staleDefs = case previousResults of |
| 98 | + Nothing => True |
| 99 | + Just (l, _) => l /= line |
| 100 | + when staleDefs $ do |
| 101 | + fuel <- gets LSPConf searchLimit |
| 102 | + existingDef <- lookupDefExact n defs.gamma |
| 103 | + case existingDef of |
| 104 | + Just None => do |
| 105 | + catch (do searchdefs <- makeDefSort (\p, n => onLine line p) fuel mostUsed n |
| 106 | + update ROpts { gdResult := Just (line, pure searchdefs) } |
| 107 | + pure ()) |
| 108 | + (\case Timeout _ => logI GenerateDefNext "Timed out" >> pure () |
| 109 | + err => logC GenerateDefNext "Unexpected error while searching" >> throw err) |
| 110 | + Just _ => logD GenerateDefNext "There is already a definition for \{show n}" >> pure () |
| 111 | + Nothing => logD GenerateDefNext "Couldn't find type declaration at line \{show line}" >> pure () |
| 112 | + |
| 113 | + Just (line', (fc, cs)) <- nextGenDef 0 |
| 114 | + | Nothing => logD GenerateDefNext "No more results" >> pure [] |
| 115 | + let l : Nat = integerToNat $ cast $ startCol (toNonEmptyFC fc) |
| 116 | + Just srcLine <- getSourceLine line' |
| 117 | + | Nothing => logE GenerateDefNext "Source line \{show line} not found" >> pure [] |
| 118 | + let (markM, srcLineUnlit) = isLitLine srcLine |
| 119 | + lines <- traverse (printClause markM l) cs |
| 120 | + |
| 121 | + put Ctxt defs |
| 122 | + |
| 123 | + let newLine = endLine loc + 1 |
| 124 | + |
| 125 | + -- Not having an easy time figuring out how to determine how many |
| 126 | + -- following lines should be replaced if there's a definition there |
| 127 | + -- already (cycling defs and not on first one). probably just use |
| 128 | + -- whitespace. |
| 129 | + defToOverride <- lookupDefExact n defs.gamma |
| 130 | + rng <- case defToOverride of |
| 131 | + Nothing => do |
| 132 | + logD GenerateDefNext "No def to override, inserting new def" |
| 133 | + pure $ MkRange (MkPosition newLine 0) (MkPosition newLine 0) -- insert |
| 134 | + (Just None) => do |
| 135 | + logD GenerateDefNext "No def to override, inserting new def" |
| 136 | + pure $ MkRange (MkPosition newLine 0) (MkPosition newLine 0) -- insert |
| 137 | + (Just (PMDef pminfo args treeCT treeRT pats)) => do |
| 138 | + src <- String.lines <$> getSource |
| 139 | + let srcFromDef = drop (integerToNat (cast line)) src |
| 140 | + let blank = findBlankLine srcFromDef line |
| 141 | + pure $ MkRange (MkPosition newLine 0) (MkPosition blank 0) -- replace |
| 142 | + (Just _) => do |
| 143 | + logE GenerateDefNext "UNEXPECTED" |
| 144 | + pure $ MkRange (MkPosition newLine 0) (MkPosition newLine 0) -- insert |
| 145 | + |
| 146 | + let docURI = params.textDocument.uri |
| 147 | + |
| 148 | + action <- do |
| 149 | + let edit = MkTextEdit rng (String.unlines lines) |
| 150 | + pure $ buildCodeAction docURI edit |
| 151 | + -- TODO: retrieve the line length efficiently |
| 152 | + pure [(MkRange (MkPosition line 0) (MkPosition line 1000), [action])] |
0 commit comments