Skip to content

Emit nested document symbols. #655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -45,6 +45,8 @@

- Fix issue where jump to definition would go to the wrong place when there are aliased identifiers in submodules https://github.com/rescript-lang/rescript-vscode/pull/653

- Fix issue where document symbols were not shown nested https://github.com/rescript-lang/rescript-vscode/pull/655

## v1.8.2

#### :rocket: New Feature
90 changes: 70 additions & 20 deletions analysis/src/DocumentSymbol.ml
Original file line number Diff line number Diff line change
@@ -26,6 +26,13 @@ let kindNumber = function

let command ~path =
let symbols = ref [] in
let addSymbol name loc kind =
let range = Utils.cmtLocToRange loc in
let symbol : Protocol.documentSymbolItem =
{name; range; kind = kindNumber kind; children = []}
in
symbols := symbol :: !symbols
in
let rec exprKind (exp : Parsetree.expression) =
match exp.pexp_desc with
| Pexp_fun _ -> Function
@@ -41,43 +48,41 @@ let command ~path =
| Ptype_variant constrDecls ->
constrDecls
|> List.iter (fun (cd : Parsetree.constructor_declaration) ->
symbols := (cd.pcd_name.txt, cd.pcd_loc, EnumMember) :: !symbols)
addSymbol cd.pcd_name.txt cd.pcd_loc EnumMember)
| Ptype_record labelDecls ->
labelDecls
|> List.iter (fun (ld : Parsetree.label_declaration) ->
symbols := (ld.pld_name.txt, ld.pld_loc, Property) :: !symbols)
addSymbol ld.pld_name.txt ld.pld_loc Property)
| _ -> ()
in
let processTypeDeclaration (td : Parsetree.type_declaration) =
symbols := (td.ptype_name.txt, td.ptype_loc, TypeParameter) :: !symbols;
addSymbol td.ptype_name.txt td.ptype_loc TypeParameter;
processTypeKind td.ptype_kind
in
let processValueDescription (vd : Parsetree.value_description) =
symbols := (vd.pval_name.txt, vd.pval_loc, Variable) :: !symbols
addSymbol vd.pval_name.txt vd.pval_loc Variable
in
let processModuleBinding (mb : Parsetree.module_binding) =
symbols := (mb.pmb_name.txt, mb.pmb_loc, Module) :: !symbols
addSymbol mb.pmb_name.txt mb.pmb_loc Module
in
let processModuleDeclaration (md : Parsetree.module_declaration) =
symbols := (md.pmd_name.txt, md.pmd_loc, Module) :: !symbols
addSymbol md.pmd_name.txt md.pmd_loc Module
in
let processExtensionConstructor (et : Parsetree.extension_constructor) =
symbols := (et.pext_name.txt, et.pext_loc, Constructor) :: !symbols
addSymbol et.pext_name.txt et.pext_loc Constructor
in
let value_binding (iterator : Ast_iterator.iterator)
(vb : Parsetree.value_binding) =
(match vb.pvb_pat.ppat_desc with
| Ppat_var {txt} | Ppat_constraint ({ppat_desc = Ppat_var {txt}}, _) ->
symbols := (txt, vb.pvb_loc, exprKind vb.pvb_expr) :: !symbols
addSymbol txt vb.pvb_loc (exprKind vb.pvb_expr)
| _ -> ());
Ast_iterator.default_iterator.value_binding iterator vb
in
let expr (iterator : Ast_iterator.iterator) (e : Parsetree.expression) =
(match e.pexp_desc with
| Pexp_letmodule ({txt}, modExpr, _) ->
symbols :=
(txt, {e.pexp_loc with loc_end = modExpr.pmod_loc.loc_end}, Module)
:: !symbols
addSymbol txt {e.pexp_loc with loc_end = modExpr.pmod_loc.loc_end} Module
| Pexp_letexception (ec, _) -> processExtensionConstructor ec
| _ -> ());
Ast_iterator.default_iterator.expr iterator e
@@ -134,12 +139,57 @@ let command ~path =
let parser = Res_driver.parsingEngine.parseInterface ~forPrinter:false in
let {Res_driver.parsetree = signature} = parser ~filename:path in
iterator.signature iterator signature |> ignore);
let result =
!symbols
|> List.rev_map (fun (name, loc, kind) ->
let range = Utils.cmtLocToRange loc in
Protocol.stringifyDocumentSymbolItem
{name; range; selectionRange = range; kind = kindNumber kind})
|> String.concat ",\n"
in
print_endline ("[\n" ^ result ^ "\n]")
let isInside
({
range =
{
start = {line = sl1; character = sc1};
end_ = {line = el1; character = ec1};
};
} :
Protocol.documentSymbolItem)
({
range =
{
start = {line = sl2; character = sc2};
end_ = {line = el2; character = ec2};
};
} :
Protocol.documentSymbolItem) =
(sl1 > sl2 || (sl1 = sl2 && sc1 >= sc2))
&& (el1 < el2 || (el1 = el2 && ec1 <= ec2))
in
let compareSymbol (s1 : Protocol.documentSymbolItem)
(s2 : Protocol.documentSymbolItem) =
let n = compare s1.range.start.line s2.range.start.line in
if n <> 0 then n
else
let n = compare s1.range.start.character s2.range.start.character in
if n <> 0 then n
else
let n = compare s1.range.end_.line s2.range.end_.line in
if n <> 0 then n
else compare s1.range.end_.character s2.range.end_.character
in
let rec addSymbolToChildren ~symbol children =
match children with
| [] -> [symbol]
| last :: rest ->
if isInside symbol last then
let newLast =
{last with children = last.children |> addSymbolToChildren ~symbol}
in
newLast :: rest
else symbol :: children
in
let rec addSortedSymbolsToChildren ~sortedSymbols children =
match sortedSymbols with
| [] -> children
| firstSymbol :: rest ->
children
|> addSymbolToChildren ~symbol:firstSymbol
|> addSortedSymbolsToChildren ~sortedSymbols:rest
in
let sortedSymbols = !symbols |> List.sort compareSymbol in
let symbolsWithChildren = [] |> addSortedSymbolsToChildren ~sortedSymbols in
print_endline (Protocol.stringifyDocumentSymbolItems symbolsWithChildren)
56 changes: 44 additions & 12 deletions analysis/src/Protocol.ml
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ type documentSymbolItem = {
name: string;
kind: int;
range: range;
selectionRange: range;
children: documentSymbolItem list;
}
type renameFile = {oldUri: string; newUri: string}
type textEdit = {range: range; newText: string}
@@ -102,22 +102,54 @@ let stringifyCompletionItem c =
| None -> null
| Some doc -> stringifyMarkupContent doc)

let stringifyHover value = Printf.sprintf {|{"contents": %s}|} (stringifyMarkupContent {kind = "markdown"; value})
let stringifyHover value =
Printf.sprintf {|{"contents": %s}|}
(stringifyMarkupContent {kind = "markdown"; value})

let stringifyLocation (h : location) =
Printf.sprintf {|{"uri": "%s", "range": %s}|} (Json.escape h.uri)
(stringifyRange h.range)

let stringifyDocumentSymbolItem (i : documentSymbolItem) =
let range = stringifyRange i.range in
Printf.sprintf
{|{
"name": "%s",
"kind": %i,
"range": %s,
"selectionRange": %s
}|}
(Json.escape i.name) i.kind range range
let stringifyDocumentSymbolItems items =
let buf = Buffer.create 10 in
let stringifyName name = Printf.sprintf "\"%s\"" (Json.escape name) in
let stringifyKind kind = string_of_int kind in
let emitStr = Buffer.add_string buf in
let emitSep () = emitStr ",\n" in
let rec emitItem ~indent item =
let openBrace = Printf.sprintf "%s{\n" indent in
let closeBrace = Printf.sprintf "\n%s}" indent in
let indent = indent ^ " " in
let emitField name s =
emitStr (Printf.sprintf "%s\"%s\": %s" indent name s)
in
emitStr openBrace;
emitField "name" (stringifyName item.name);
emitSep ();
emitField "kind" (stringifyKind item.kind);
emitSep ();
emitField "range" (stringifyRange item.range);
emitSep ();
emitField "selectionRange" (stringifyRange item.range);
if item.children <> [] then (
emitSep ();
emitField "children" "[\n";
emitBody ~indent (List.rev item.children);
emitStr "]");
emitStr closeBrace
and emitBody ~indent items =
match items with
| [] -> ()
| item :: rest ->
emitItem ~indent item;
if rest <> [] then emitSep ();
emitBody ~indent rest
in
let indent = "" in
emitStr "[\n";
emitBody ~indent (List.rev items);
emitStr "\n]";
Buffer.contents buf

let stringifyRenameFile {oldUri; newUri} =
Printf.sprintf {|{
165 changes: 85 additions & 80 deletions analysis/tests/src/expected/DocumentSymbol.res.txt
Original file line number Diff line number Diff line change
@@ -1,100 +1,105 @@
DocumentSymbol src/DocumentSymbol.res
[
{
"name": "MyList",
"kind": 2,
"range": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 25}},
"selectionRange": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 25}}
"name": "MyList",
"kind": 2,
"range": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 25}},
"selectionRange": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 25}}
},
{
"name": "Dep",
"kind": 2,
"range": {"start": {"line": 2, "character": 7}, "end": {"line": 7, "character": 1}},
"selectionRange": {"start": {"line": 2, "character": 7}, "end": {"line": 7, "character": 1}}
"name": "Dep",
"kind": 2,
"range": {"start": {"line": 2, "character": 7}, "end": {"line": 7, "character": 1}},
"selectionRange": {"start": {"line": 2, "character": 7}, "end": {"line": 7, "character": 1}},
"children": [
{
"name": "customDouble",
"kind": 12,
"range": {"start": {"line": 6, "character": 2}, "end": {"line": 6, "character": 35}},
"selectionRange": {"start": {"line": 6, "character": 2}, "end": {"line": 6, "character": 35}}
}]
},
{
"name": "customDouble",
"kind": 12,
"range": {"start": {"line": 6, "character": 2}, "end": {"line": 6, "character": 35}},
"selectionRange": {"start": {"line": 6, "character": 2}, "end": {"line": 6, "character": 35}}
"name": "Lib",
"kind": 2,
"range": {"start": {"line": 9, "character": 7}, "end": {"line": 12, "character": 1}},
"selectionRange": {"start": {"line": 9, "character": 7}, "end": {"line": 12, "character": 1}},
"children": [
{
"name": "foo",
"kind": 12,
"range": {"start": {"line": 10, "character": 2}, "end": {"line": 10, "character": 55}},
"selectionRange": {"start": {"line": 10, "character": 2}, "end": {"line": 10, "character": 55}}
},
{
"name": "next",
"kind": 12,
"range": {"start": {"line": 11, "character": 2}, "end": {"line": 11, "character": 48}},
"selectionRange": {"start": {"line": 11, "character": 2}, "end": {"line": 11, "character": 48}}
}]
},
{
"name": "Lib",
"kind": 2,
"range": {"start": {"line": 9, "character": 7}, "end": {"line": 12, "character": 1}},
"selectionRange": {"start": {"line": 9, "character": 7}, "end": {"line": 12, "character": 1}}
"name": "op",
"kind": 13,
"range": {"start": {"line": 14, "character": 0}, "end": {"line": 14, "character": 16}},
"selectionRange": {"start": {"line": 14, "character": 0}, "end": {"line": 14, "character": 16}}
},
{
"name": "foo",
"kind": 12,
"range": {"start": {"line": 10, "character": 2}, "end": {"line": 10, "character": 55}},
"selectionRange": {"start": {"line": 10, "character": 2}, "end": {"line": 10, "character": 55}}
"name": "ForAuto",
"kind": 2,
"range": {"start": {"line": 16, "character": 7}, "end": {"line": 20, "character": 1}},
"selectionRange": {"start": {"line": 16, "character": 7}, "end": {"line": 20, "character": 1}},
"children": [
{
"name": "t",
"kind": 26,
"range": {"start": {"line": 17, "character": 2}, "end": {"line": 17, "character": 14}},
"selectionRange": {"start": {"line": 17, "character": 2}, "end": {"line": 17, "character": 14}}
},
{
"name": "abc",
"kind": 12,
"range": {"start": {"line": 18, "character": 2}, "end": {"line": 18, "character": 32}},
"selectionRange": {"start": {"line": 18, "character": 2}, "end": {"line": 18, "character": 32}}
},
{
"name": "abd",
"kind": 12,
"range": {"start": {"line": 19, "character": 2}, "end": {"line": 19, "character": 32}},
"selectionRange": {"start": {"line": 19, "character": 2}, "end": {"line": 19, "character": 32}}
}]
},
{
"name": "next",
"kind": 12,
"range": {"start": {"line": 11, "character": 2}, "end": {"line": 11, "character": 48}},
"selectionRange": {"start": {"line": 11, "character": 2}, "end": {"line": 11, "character": 48}}
"name": "fa",
"kind": 16,
"range": {"start": {"line": 22, "character": 0}, "end": {"line": 22, "character": 22}},
"selectionRange": {"start": {"line": 22, "character": 0}, "end": {"line": 22, "character": 22}}
},
{
"name": "op",
"kind": 13,
"range": {"start": {"line": 14, "character": 0}, "end": {"line": 14, "character": 16}},
"selectionRange": {"start": {"line": 14, "character": 0}, "end": {"line": 14, "character": 16}}
"name": "O",
"kind": 2,
"range": {"start": {"line": 24, "character": 7}, "end": {"line": 29, "character": 1}},
"selectionRange": {"start": {"line": 24, "character": 7}, "end": {"line": 29, "character": 1}},
"children": [
{
"name": "Comp",
"kind": 2,
"range": {"start": {"line": 25, "character": 9}, "end": {"line": 28, "character": 3}},
"selectionRange": {"start": {"line": 25, "character": 9}, "end": {"line": 28, "character": 3}},
"children": [
{
"name": "make",
"kind": 12,
"range": {"start": {"line": 27, "character": 4}, "end": {"line": 27, "character": 98}},
"selectionRange": {"start": {"line": 27, "character": 4}, "end": {"line": 27, "character": 98}}
}]
}]
},
{
"name": "ForAuto",
"kind": 2,
"range": {"start": {"line": 16, "character": 7}, "end": {"line": 20, "character": 1}},
"selectionRange": {"start": {"line": 16, "character": 7}, "end": {"line": 20, "character": 1}}
},
{
"name": "t",
"kind": 26,
"range": {"start": {"line": 17, "character": 2}, "end": {"line": 17, "character": 14}},
"selectionRange": {"start": {"line": 17, "character": 2}, "end": {"line": 17, "character": 14}}
},
{
"name": "abc",
"kind": 12,
"range": {"start": {"line": 18, "character": 2}, "end": {"line": 18, "character": 32}},
"selectionRange": {"start": {"line": 18, "character": 2}, "end": {"line": 18, "character": 32}}
},
{
"name": "abd",
"kind": 12,
"range": {"start": {"line": 19, "character": 2}, "end": {"line": 19, "character": 32}},
"selectionRange": {"start": {"line": 19, "character": 2}, "end": {"line": 19, "character": 32}}
},
{
"name": "fa",
"kind": 16,
"range": {"start": {"line": 22, "character": 0}, "end": {"line": 22, "character": 22}},
"selectionRange": {"start": {"line": 22, "character": 0}, "end": {"line": 22, "character": 22}}
},
{
"name": "O",
"kind": 2,
"range": {"start": {"line": 24, "character": 7}, "end": {"line": 29, "character": 1}},
"selectionRange": {"start": {"line": 24, "character": 7}, "end": {"line": 29, "character": 1}}
},
{
"name": "Comp",
"kind": 2,
"range": {"start": {"line": 25, "character": 9}, "end": {"line": 28, "character": 3}},
"selectionRange": {"start": {"line": 25, "character": 9}, "end": {"line": 28, "character": 3}}
},
{
"name": "make",
"kind": 12,
"range": {"start": {"line": 27, "character": 4}, "end": {"line": 27, "character": 98}},
"selectionRange": {"start": {"line": 27, "character": 4}, "end": {"line": 27, "character": 98}}
},
{
"name": "zzz",
"kind": 16,
"range": {"start": {"line": 31, "character": 0}, "end": {"line": 31, "character": 12}},
"selectionRange": {"start": {"line": 31, "character": 0}, "end": {"line": 31, "character": 12}}
"name": "zzz",
"kind": 16,
"range": {"start": {"line": 31, "character": 0}, "end": {"line": 31, "character": 12}},
"selectionRange": {"start": {"line": 31, "character": 0}, "end": {"line": 31, "character": 12}}
}
]