1:- module(pls_index_completions, [
    2  completions_for_position/3,
    3  suggestions_for_position/3,
    4  identifier_for_position/3
    5]).    6
    7:- use_module(docs).    8:- use_module(documents).    9
   10:- use_module(library(log4p)).   11
   12completions_for_position(URI, Position, Completions) :-
   13  % we usually are at the end, so not pointing at a character
   14  % so go back 1
   15  prev_position(Position, Previous),
   16  ( suggestions_for_position(URI, Previous, Suggestions)
   17    -> (
   18        findall(
   19          _{
   20            label: Label,
   21            kind: 3,
   22            documentation: Docs,
   23            insertText: Signature,
   24            insertMode: 2
   25          },
   26          (
   27            member(Suggestion, Suggestions),
   28            Suggestion = Name/Arity,
   29            term_string(Suggestion, Label),
   30            get_document_item(_DefURI, _DefRange, signature(Name/Arity, Signature)),
   31            get_docs(Suggestion, Docs)
   32            ),
   33          Completions
   34        )
   35      )
   36    ; Completions = []
   37    ).
   38
   39suggestions_for_position(URI, Position, Suggestions) :-
   40  identifier_for_position(URI, Position, Identifier),
   41  atom_length(Identifier, Length),
   42  (Length >= 3
   43    -> findall(
   44        Name/Arity,
   45        (
   46          get_document_item(_DefURI, _DefRange, defines(Name/Arity)),
   47          sub_atom(Name, _Before, Length, _After, Identifier)
   48          ),
   49        Suggestions
   50      )
   51    ; fail
   52    ).
   53
   54identifier_for_position(URI, Position, Identifier) :-
   55  find_identifier_start(URI, Position, Start),
   56  find_identifier_end(URI, Position, End),
   57  Length is End.character - Start.character + 1,
   58  content_at_position(URI, Start, Length, Text),
   59  atom_string(Identifier, Text).
   60
   61find_identifier_start(URI, Position, Start) :-
   62  Position.character = 0,
   63  character_at_position(URI, Position, Character),
   64  identifier_character(Character),
   65  Start = Position,
   66  !.
   67
   68find_identifier_start(URI, Position, Start) :-
   69  character_at_position(URI, Position, Character),
   70  identifier_character(Character),
   71  prev_position(Position, Previous),
   72  character_at_position(URI, Previous, PreviousCharacter),
   73  \+identifier_character(PreviousCharacter),
   74  Start = Position,
   75  !.
   76
   77find_identifier_start(URI, Position, Start) :-
   78  character_at_position(URI, Position, Character),
   79  identifier_character(Character),
   80  prev_position(Position, Previous),
   81  find_identifier_start(URI, Previous, Start),
   82  !.
   83
   84find_identifier_end(URI, Position, End) :-
   85  character_at_position(URI, Position, Character),
   86  identifier_character(Character),
   87  next_position(Position, NextPosition),
   88  character_at_position(URI, NextPosition, NextCharacter),
   89  \+identifier_character(NextCharacter),
   90  End = Position,
   91  !.
   92
   93find_identifier_end(URI, Position, End) :-
   94  character_at_position(URI, Position, Character),
   95  identifier_character(Character),
   96  next_position(Position, NextPosition),
   97  find_identifier_end(URI, NextPosition, End),
   98  !.
   99
  100prev_position(Position, _Previous) :-
  101  Position.character = 0, fail.
  102
  103prev_position(Position, Previous) :-
  104  PreviousCharacter is Position.character - 1,
  105  Previous = _{
  106    line: Position.line,
  107    character: PreviousCharacter
  108    }.
  109
  110next_position(Position, Next) :-
  111  NextCharacter is Position.character + 1,
  112  Next = _{
  113    line: Position.line,
  114    character: NextCharacter
  115    }.
  116
  117character_at_position(URI, Position, Character) :-
  118  content_at_position(URI, Position, 1, Character).
  119
  120content_at_position(URI, Position, Length, Text) :-
  121  Line = Position.line,
  122  CharacterPos = Position.character,
  123  get_document_line_position(URI, Line, LineStart),
  124  Offset is LineStart + CharacterPos,
  125  with_content(URI, In,
  126    (
  127      seek(In, Offset, bof, _),
  128      read_string(In, Length, Text),
  129      (Text \= [])
  130      )
  131    ).
  132
  133identifier_character(Character) :-
  134  letter(Character).
  135
  136identifier_character(Character) :-
  137  digit(Character).
  138
  139identifier_character("_").
  140
  141digit(Character) :-
  142  Character >= "0", Character =< "9".
  143
  144letter(Character) :-
  145  Character >= "a", Character =< "z".
  146
  147letter(Character) :-
  148  Character >= "A", Character =< "Z"