summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorNikolay Nikolov <nickysn@gmail.com>2023-11-04 09:51:09 +0200
committerGitHub <noreply@github.com>2023-11-04 08:51:09 +0100
commit3f2b9c8bcf9faf30b6844e5222ffe6ec501064e8 (patch)
tree2a2633cfe9de1e8262ee9f9b251a13e7110c8ed7 /compiler
parent95e5ad6927af309552857686bf2c74bfac36cbb7 (diff)
downloadNim-3f2b9c8bcf9faf30b6844e5222ffe6ec501064e8.tar.gz
Inlay hints support (#22896)
This adds inlay hints support to nimsuggest. It adds a new command to
nimsuggest, called 'inlayHints'.

Currently, it provides type information to 'var' and 'let' variables. In
the future, inlay hints can also be added for 'const' and for function
parameters. The protocol also reserves space for a tooltip field, which
is not used, yet, but support for it can be added in the future, without
further changing the protocol.

The change includes refactoring to allow the 'inlayHints' command to
return a completely different structure, compared to the other
nimsuggest commands. This will allow other future commands to have
custom return types as well. All the previous commands return the same
structure as before, so perfect backwards compatibility is maintained.

To use this feature, an update to the nim language server, as well as
the VS code extension is needed.

Related PRs:
nimlangserver: https://github.com/nim-lang/langserver/pull/53
VS code extension: https://github.com/saem/vscode-nim/pull/134

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim1
-rw-r--r--compiler/modulegraphs.nim1
-rw-r--r--compiler/options.nim18
-rw-r--r--compiler/semstmts.nim4
-rw-r--r--compiler/suggest.nim114
5 files changed, 96 insertions, 42 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5ee9afa02..661a82703 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -903,6 +903,7 @@ type
     info*: TLineInfo
     when defined(nimsuggest):
       endInfo*: TLineInfo
+      hasUserSpecifiedType*: bool  # used for determining whether to display inlay type hints
     owner*: PSym
     flags*: TSymFlags
     ast*: PNode               # syntax tree of proc, iterator, etc.:
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index ba636eb5a..5c23325e8 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -57,6 +57,7 @@ type
   SymInfoPair* = object
     sym*: PSym
     info*: TLineInfo
+    isDecl*: bool
 
   PipelinePass* = enum
     NonePass
diff --git a/compiler/options.nim b/compiler/options.nim
index f2d93a9b3..704248d78 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -199,7 +199,7 @@ type
   IdeCmd* = enum
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
     ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
-    ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand
+    ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand, ideInlayHints
 
   Feature* = enum  ## experimental features; DO NOT RENAME THESE!
     dotOperators,
@@ -288,9 +288,24 @@ type
     version*: int
     endLine*: uint16
     endCol*: int
+    inlayHintInfo*: SuggestInlayHint
 
   Suggestions* = seq[Suggest]
 
+  SuggestInlayHintKind* = enum
+    sihkType = "Type",
+    sihkParameter = "Parameter"
+
+  SuggestInlayHint* = ref object
+    kind*: SuggestInlayHintKind
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    label*: string
+    paddingLeft*: bool
+    paddingRight*: bool
+    allowInsert*: bool
+    tooltip*: string
+
   ProfileInfo* = object
     time*: float
     count*: int
@@ -1071,6 +1086,7 @@ proc `$`*(c: IdeCmd): string =
   of ideRecompile: "recompile"
   of ideChanged: "changed"
   of ideType: "type"
+  of ideInlayHints: "inlayHints"
 
 proc floatInt64Align*(conf: ConfigRef): int16 =
   ## Returns either 4 or 8 depending on reasons.
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index a4de874ba..0196b9d03 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -672,9 +672,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       addToVarSection(c, result, b)
       continue
 
+    var hasUserSpecifiedType = false
     var typ: PType = nil
     if a[^2].kind != nkEmpty:
       typ = semTypeNode(c, a[^2], nil)
+      hasUserSpecifiedType = true
 
     var typFlags: TTypeAllowedFlags = {}
 
@@ -746,6 +748,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           addToVarSection(c, result, n, a)
           continue
         var v = semIdentDef(c, a[j], symkind, false)
+        when defined(nimsuggest):
+          v.hasUserSpecifiedType = hasUserSpecifiedType
         styleCheckDef(c, v)
         onDef(a[j].info, v)
         if sfGenSym notin v.flags:
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 5714c6d21..1d84fada5 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -107,7 +107,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int
       result = 0
   elif ident[0] in linter.Letters and ident[^1] != '=':
     result = identLen(line, column)
-    if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0:
+    if cmpIgnoreStyle(line[column..column + result - 1], ident[0..min(result-1,len(ident)-1)]) != 0:
       result = 0
   else:
     var sourceIdent: string = ""
@@ -177,7 +177,7 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
     result.filePath = toFullPath(g.config, infox)
     result.line = toLinenumber(infox)
     result.column = toColumn(infox)
-    result.tokenLen = if section != ideHighlight:
+    result.tokenLen = if section notin {ideHighlight, ideInlayHints}:
                         s.name.s.len
                       else:
                         getTokenLenFromSource(g.config, s.name.s, infox)
@@ -185,50 +185,82 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
   result.endLine = endLine
   result.endCol = endCol
 
-proc `$`*(suggest: Suggest): string =
-  result = $suggest.section
+proc `$`*(suggest: SuggestInlayHint): string =
+  result = $suggest.kind
   result.add(sep)
-  if suggest.section == ideHighlight:
-    if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
-      result.add("skGlobalVar")
-    elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
-      result.add("skGlobalLet")
-    else:
-      result.add($suggest.symkind.TSymKind)
-    result.add(sep)
-    result.add($suggest.line)
-    result.add(sep)
-    result.add($suggest.column)
-    result.add(sep)
-    result.add($suggest.tokenLen)
+  result.add($suggest.line)
+  result.add(sep)
+  result.add($suggest.column)
+  result.add(sep)
+  result.add(suggest.label)
+  result.add(sep)
+  result.add($suggest.paddingLeft)
+  result.add(sep)
+  result.add($suggest.paddingRight)
+  result.add(sep)
+  result.add($suggest.allowInsert)
+  result.add(sep)
+  result.add(suggest.tooltip)
+
+proc `$`*(suggest: Suggest): string =
+  if suggest.section == ideInlayHints:
+    result = $suggest.inlayHintInfo
   else:
-    result.add($suggest.symkind.TSymKind)
-    result.add(sep)
-    if suggest.qualifiedPath.len != 0:
-      result.add(suggest.qualifiedPath.join("."))
-    result.add(sep)
-    result.add(suggest.forth)
-    result.add(sep)
-    result.add(suggest.filePath)
+    result = $suggest.section
     result.add(sep)
-    result.add($suggest.line)
-    result.add(sep)
-    result.add($suggest.column)
-    result.add(sep)
-    when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
-      result.add(suggest.doc.escape)
-    if suggest.version == 0 or suggest.version == 3:
+    if suggest.section == ideHighlight:
+      if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
+        result.add("skGlobalVar")
+      elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
+        result.add("skGlobalLet")
+      else:
+        result.add($suggest.symkind.TSymKind)
+      result.add(sep)
+      result.add($suggest.line)
+      result.add(sep)
+      result.add($suggest.column)
+      result.add(sep)
+      result.add($suggest.tokenLen)
+    else:
+      result.add($suggest.symkind.TSymKind)
       result.add(sep)
-      result.add($suggest.quality)
-      if suggest.section == ideSug:
+      if suggest.qualifiedPath.len != 0:
+        result.add(suggest.qualifiedPath.join("."))
+      result.add(sep)
+      result.add(suggest.forth)
+      result.add(sep)
+      result.add(suggest.filePath)
+      result.add(sep)
+      result.add($suggest.line)
+      result.add(sep)
+      result.add($suggest.column)
+      result.add(sep)
+      when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
+        result.add(suggest.doc.escape)
+      if suggest.version == 0 or suggest.version == 3:
         result.add(sep)
-        result.add($suggest.prefix)
+        result.add($suggest.quality)
+        if suggest.section == ideSug:
+          result.add(sep)
+          result.add($suggest.prefix)
 
-  if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
-    result.add(sep)
-    result.add($suggest.endLine)
-    result.add(sep)
-    result.add($suggest.endCol)
+    if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
+      result.add(sep)
+      result.add($suggest.endLine)
+      result.add(sep)
+      result.add($suggest.endCol)
+
+proc suggestToSuggestInlayHint*(sug: Suggest): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkType,
+    line: sug.line,
+    column: sug.column + sug.tokenLen,
+    label: ": " & sug.forth,
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: true,
+    tooltip: ""
+  )
 
 proc suggestResult*(conf: ConfigRef; s: Suggest) =
   if not isNil(conf.suggestionResultHook):
@@ -537,7 +569,7 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
   ## misnamed: should be 'symDeclared'
   let conf = g.config
   when defined(nimsuggest):
-    g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info)
+    g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info, isDecl: isDecl)
 
     if conf.suggestVersion == 0:
       if s.allUsages.len == 0: