import github::github_curl
import template
+import opts
redef class Object
# Factorize cast
redef class GithubCurl
# Get a given pull request (PR)
- fun getpr(number: Int): JsonObject
+ fun getpr(repo: String, number: Int): nullable JsonObject
do
- var pr = get_and_check("https://api.github.com/repos/nitlang/nit/pulls/{number}")
+ var ir = get_and_check("https://api.github.com/repos/{repo}/issues/{number}")
+ var irm = ir.json_as_map
+ if not irm.has_key("pull_request") then return null
+ var pr = get_and_check("https://api.github.com/repos/{repo}/pulls/{number}")
var prm = pr.json_as_map
var sha = prm["head"].json_as_map["sha"].to_s
- var statuses = get_and_check("https://api.github.com/repos/nitlang/nit/statuses/{sha}")
+ var statuses = get_and_check("https://api.github.com/repos/{repo}/commits/{sha}/status")
+ statuses = statuses.json_as_map
prm["statuses"] = statuses
print "{prm["title"].to_s}: by {prm["user"].json_as_map["login"].to_s} (# {prm["number"].to_s})"
var mergeable = prm["mergeable"]
else
print "\tmergeable: unknown"
end
- var st = prm["statuses"].json_as_a
- if not st.is_empty then
- print "\tstatus: {st[0].json_as_map["state"].to_s}"
- else
+ var state = statuses["state"]
+ if state == null then
print "\tstatus: not tested"
+ else
+ print "\tstatus: {state}"
+ var sts = statuses["statuses"].json_as_a
+ for st in sts do
+ st = st.json_as_map
+ var ctx = st["context"].to_s
+ state = st["state"].to_s
+ print "\tstatus {ctx}: {state}"
+ prm["status-{ctx}"] = state
+ end
end
return prm
end
# Get reviewers of a PR
- fun getrev(pr: JsonObject): Array[String]
+ fun getrev(repo: String, pr: JsonObject): Array[String]
do
var number = pr["number"].as(Int)
var user = pr["user"].json_as_map["login"].as(String)
var comments = new Array[nullable Object]
- comments.add_all(get_and_check("https://api.github.com/repos/nitlang/nit/issues/{number}/comments").json_as_a)
- comments.add_all(get_and_check("https://api.github.com/repos/nitlang/nit/pulls/{number}/comments").json_as_a)
+ comments.add_all(get_and_check("https://api.github.com/repos/{repo}/issues/{number}/comments").json_as_a)
+ comments.add_all(get_and_check("https://api.github.com/repos/{repo}/pulls/{number}/comments").json_as_a)
var logins = new Array[String]
for c in comments do
var cm = c.json_as_map
if "NIT_TESTING".environ == "true" then exit 0
-var auth = get_github_oauth
+var opt_repo = new OptionString("Repository (e.g. nitlang/nit)", "-r", "--repo")
+var opt_auth = new OptionString("OAuth token", "--auth")
+var opt_query = new OptionString("Query to get issues (e.g. label=ok_will_merge)", "-q", "--query")
+var opt_keepgoing = new OptionBool("Skip merge conflicts", "-k", "--keep-going")
+var opt_all = new OptionBool("Merge all", "-a", "--all")
+var opt_status = new OptionArray("A status context that must be \"success\" (e.g. default)", "--status")
+var opts = new OptionContext
+opts.add_option(opt_repo, opt_auth, opt_query, opt_status, opt_all, opt_keepgoing)
+
+opts.parse(sys.args)
+var args = opts.rest
+var auth = opt_auth.value or else ""
+if auth == "" then auth = get_github_oauth
if auth == "" then
print "Warning: no github oauth token, you can configure one with"
print " git config --add github.oauthtoken MYOAUTHTOKEN"
end
+var repo = opt_repo.value or else "nitlang/nit"
+
+var query = opt_query.value or else "labels=ok_will_merge"
+
var curl = new GithubCurl(auth, "Merge-o-matic (nitlang/nit)")
-if args.length != 1 then
+if args.is_empty then
# Without args, list `ok_will_merge`
- var x = curl.get_and_check("https://api.github.com/repos/nitlang/nit/issues?labels=ok_will_merge")
+ var x = curl.get_and_check("https://api.github.com/repos/{repo}/issues?{query}")
+ var list = new Array[String]
for y in x.json_as_a do
var number = y.json_as_map["number"].as(Int)
- curl.getpr(number)
- end
-else
+ var pr = curl.getpr(repo, number)
+ if pr == null then continue
+ for ctx in opt_status.value do
+ if pr.get_or_null("status-{ctx}") != "success" then
+ print "No \"success\" for {ctx}. Skip."
+ continue label
+ end
+ end
+ list.add number.to_s
+ end label
+
+ if not opt_all.value then return
+ args = list
+end
+
+for arg in args do
# With a arg, merge the PR
- var number = args.first.to_i
- var pr = curl.getpr(number)
- var revs = curl.getrev(pr)
+ var number = arg.to_i
+ var pr = curl.getpr(repo, number)
+ if pr == null then
+ print "Not a PR: {number}"
+ return
+ end
+ var revs = curl.getrev(repo, pr)
var mergemsg = new Template
mergemsg.add "Merge: {pr["title"].to_s}\n\n"
print "Commit {sha} not in local repository; did you fetch github?"
return
end
+ if system("git merge-base --is-ancestor {sha} HEAD") == 0 then
+ print "Is already merged."
+ continue
+ end
if system("git merge --no-ff --no-commit {sha}") != 0 then
+ if opt_keepgoing.value then
+ system("git reset --merge")
+ continue
+ end
system("cp mergemsg `git rev-parse --git-dir`/MERGE_MSG")
print "Problem during merge... Let's do the commit manually."
return
## Core Basic Types and Operations
-[[doc:kernel]]
+[[doc: kernel]]
### Object
-[[doc:Object]]
+[[doc: Object]]
#### Equality
-[[doc:Object::==]]
-[[doc:Object::!=]]
-[[doc:Object::hash]]
-[[doc:Object::is_same_instance]]
-[[doc:Object::object_id]]
+[[doc: core::Object::==]]
+[[doc: core::Object::!=]]
+[[doc: core::Object::hash]]
+[[doc: core::Object::is_same_instance]]
+[[doc: core::Object::object_id]]
#### Debuging
-[[doc:Object::output]]
-[[doc:Object::output_class_name]]
-[[doc:Object::is_same_type]]
+[[doc: core::Object::output]]
+[[doc: core::Object::output_class_name]]
+[[doc: core::Object::is_same_type]]
### Sys
-[[doc:Sys]]
+[[doc: Sys]]
#### Program Execution
-[[doc:Sys::main]]
-[[doc:Sys::run]]
+[[doc: core::Sys::main]]
+[[doc: core::Sys::run]]
### Other
-[[list:kernel]]
+[[list: kernel]]
## Core Collections
-[[doc:collection]]
+[[doc: collection]]
## String and Text manipulation
-[[doc:text]]
+[[doc: text]]
do
for i in substrings do s.write_native(i.to_cstring, 0, i.byte_length)
end
-end
-redef class String
# return true if a file with this names exists
fun file_exists: Bool do return to_cstring.file_exists
+end
+redef class String
# The status of a file. see POSIX stat(2).
fun file_stat: nullable FileStat
do
path.add('/')
end
var error: nullable Error = null
- for d in dirs do
+ for i in [0 .. dirs.length - 1[ do
+ var d = dirs[i]
if d.is_empty then continue
path.append(d)
path.add('/')
- var res = path.to_s.to_cstring.file_mkdir(mode)
+ if path.file_exists then continue
+ var res = path.to_cstring.file_mkdir(mode)
if not res and error == null then
error = new IOError("Cannot create directory `{path}`: {sys.errno.strerror}")
end
end
+ var res = self.to_cstring.file_mkdir(mode)
+ if not res and error == null then
+ error = new IOError("Cannot create directory `{path}`: {sys.errno.strerror}")
+ end
return error
end
### Authentification
-[[doc: GithubAPI::auth]]
+[[doc: auth]]
Token can also be recovered from user config with `get_github_oauth`.
### Other data
-[[list: api]]
+[[list: github::api]]
### Advanced uses
#### Custom requests
-[[doc: GithubAPI::get]]
+[[doc: github::GithubAPI::get]]
#### Change the user agent
-[[doc: GithubAPI::user_agent]]
+[[doc: github::GithubAPI::user_agent]]
#### Debugging
redef fun deserialize_attribute(name, static_type)
do
- assert not path.is_empty # This is an internal error, abort
+ if path.is_empty then
+ # The was a parsing error or the root is not an object
+ if not root isa Error then
+ errors.add new Error("Deserialization Error: parsed JSON value is not an object.")
+ end
+ return null
+ end
+
var current = path.last
if not current.keys.has(name) then
[[doc: NLPDocument]]
-[[doc: NLPDocument::from_xml]]
-[[doc: NLPDocument::from_xml_file]]
-[[doc: NLPDocument::sentences]]
+[[doc: nlp::NLPDocument::from_xml]]
+[[doc: nlp::NLPDocument::from_xml_file]]
+[[doc: nlp::NLPDocument::sentences]]
### NLPSentence
[[doc: NLPSentence]]
-[[doc: NLPSentence::tokens]]
+[[doc: nlp::NLPSentence::tokens]]
### NLPToken
[[doc: NLPToken]]
-[[doc: NLPToken::word]]
-[[doc: NLPToken::lemma]]
-[[doc: NLPToken::pos]]
+[[doc: nlp::NLPToken::word]]
+[[doc: nlp::NLPToken::lemma]]
+[[doc: nlp::NLPToken::pos]]
### NLP Processor
[[doc: NLPProcessor]]
-[[doc: NLPProcessor::java_cp]]
+[[doc: nlp::NLPProcessor::java_cp]]
-[[doc: NLPProcessor::process]]
-[[doc: NLPProcessor::process_file]]
-[[doc: NLPProcessor::process_files]]
+[[doc: nlp::NLPProcessor::process]]
+[[doc: nlp::NLPProcessor::process_file]]
+[[doc: nlp::NLPProcessor::process_files]]
## Vector Space Model
[[doc: NLPVector]]
-[[doc: NLPDocument::vector]]
+[[doc: vector]]
-[[doc: NLPVector::cosine_similarity]]
+[[doc: nlp::NLPVector::cosine_similarity]]
## NitNLP binary
# When the first iterator is terminated, the second is started.
#
# assert ([1..20[.iterator + [20..40[.iterator).to_a == ([1..40[).to_a
+ #
+ # SEE: `Iterator2`
fun +(other: Iterator[E]): Iterator[E]
do
return new PipeJoin[E](self, other)
end
end
+# Concatenates a sequence of iterators.
+#
+# Wraps an iterator of sub-iterators and iterates over the elements of the
+# sub-iterators.
+#
+# ~~~nit
+# var i: Iterator[Int]
+# var empty = new Array[Int]
+#
+# i = new Iterator2[Int]([
+# [1, 2, 3].iterator,
+# empty.iterator,
+# [4, 5].iterator
+# ].iterator)
+# assert i.to_a == [1, 2, 3, 4, 5]
+#
+# i = new Iterator2[Int]([
+# empty.iterator,
+# [42].iterator,
+# empty.iterator
+# ].iterator)
+# assert i.to_a == [42]
+# ~~~
+#
+# SEE: `Iterator::+`
+class Iterator2[E]
+ super Iterator[E]
+
+ # The inner iterator over sub-iterators.
+ var inner: Iterator[Iterator[E]]
+
+ redef fun finish
+ do
+ var i = current_iterator
+ if i != null then i.finish
+ end
+
+ redef fun is_ok
+ do
+ var i = current_iterator
+ if i == null then return false
+ return i.is_ok
+ end
+
+ redef fun item
+ do
+ var i = current_iterator
+ assert i != null
+ return i.item
+ end
+
+ redef fun next
+ do
+ var i = current_iterator
+ assert i != null
+ i.next
+ end
+
+ redef fun start
+ do
+ var i = current_iterator
+ if i != null then i.start
+ end
+
+ private var previous_iterator: nullable Iterator[E] = null
+
+ private fun current_iterator: nullable Iterator[E]
+ do
+ if previous_iterator == null then
+ # Get the first sub-iterator.
+ if inner.is_ok then
+ previous_iterator = inner.item
+ previous_iterator.start
+ inner.next
+ else
+ return null
+ end
+ end
+ # Get the first sub-iterator that has a current item.
+ while inner.is_ok and not previous_iterator.is_ok do
+ previous_iterator.finish
+ previous_iterator = inner.item
+ previous_iterator.start
+ inner.next
+ end
+ return previous_iterator
+ end
+end
+
# Wraps an iterator to skip nulls.
#
# ~~~nit
import doc_commands
import doc_poset
import doc::console_templates
+import model::model_index
# Nitx handles console I/O.
#
return
end
var res = query.perform(self, doc)
- var page = query.make_results(self, res)
+ var suggest = null
+ if res.is_empty then
+ suggest = query.suggest(self, doc)
+ end
+ var page = query.make_results(self, res, suggest)
print page.write_to_string
end
end
# Looks up the `doc` model and returns possible matches.
fun perform(nitx: Nitx, doc: DocModel): Array[NitxMatch] is abstract
+ # Looks up the `doc` model and returns possible suggestions.
+ fun suggest(nitx: Nitx, doc: DocModel): nullable Array[MEntity] do
+ return find_suggestions(doc, args.first)
+ end
+
# Pretty prints the results for the console.
- fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do
+ fun make_results(nitx: Nitx, results: Array[NitxMatch], suggest: nullable Array[MEntity]): DocPage do
var page = new DocPage("results", "Results")
- page.root.add_child(new QueryResultArticle("results", "Results", self, results))
+ page.root.add_child(new QueryResultArticle("results", "Results", self, results, suggest))
return page
end
+
+ # Lookup mentities based on a `query` string.
+ #
+ # 1- lookup by first name (returns always one value)
+ # 2- lookup by name (can return conflicts)
+ fun find_mentities(doc: DocModel, query: String): Array[MEntityMatch] do
+ var res = new Array[MEntityMatch]
+
+ # First lookup by full_name
+ var mentity = doc.mentity_by_full_name(query)
+ if mentity != null then
+ res.add new MEntityMatch(self, mentity)
+ return res
+ end
+
+ # If no results, lookup by name
+ for m in doc.mentities_by_name(query) do
+ res.add new MEntityMatch(self, m)
+ end
+
+ return res
+ end
+
+ # Suggest some mentities based on a `query` string.
+ fun find_suggestions(doc: DocModel, query: String): Array[MEntity] do
+ return doc.find(query, 3)
+ end
end
# Something that matches a `DocCommand`.
end
redef class CommentCommand
- redef fun perform(nitx, doc) do
- var name = args.first
- var res = new Array[NitxMatch]
- for mentity in doc.mentities_by_name(name) do
- res.add new MEntityMatch(self, mentity)
- end
- return res
- end
+ redef fun perform(nitx, doc) do return find_mentities(doc, args.first)
- redef fun make_results(nitx, results) do
+ redef fun make_results(nitx, results, suggest) do
var len = results.length
if len == 1 then
var res = results.first.as(MEntityMatch)
return res
end
- redef fun make_results(nitx, results) do
+ redef fun make_results(nitx, results, suggest) do
var len = results.length
# FIXME how to render the pager for one worded namespaces like "core"?
if len == 1 then
abstract class HierarchiesQuery
super DocCommand
- redef fun make_results(nitx, results) do
+ redef fun make_results(nitx, results, suggest) do
var page = new DocPage("hierarchy", "Hierarchy")
for result in results do
if not result isa PageMatch then continue
return res
end
# else, lookup the model by name
- for mentity in doc.mentities_by_name(name) do
- if mentity isa MClass then continue
- if mentity isa MProperty then continue
- res.add new CodeMatch(self, mentity.cs_location, mentity.cs_source_code)
+ for match in find_mentities(doc, name) do
+ if match.mentity isa MClass then continue
+ if match.mentity isa MProperty then continue
+ res.add new CodeMatch(self, match.mentity.cs_location, match.mentity.cs_source_code)
end
return res
end
- redef fun make_results(nitx, results) do
+ redef fun make_results(nitx, results, suggest) do
var page = new DocPage("results", "Code Results")
for res in results do
page.add new CodeQueryArticle("results", "Results", self, res.as(CodeMatch))
# Results to display.
var results: Array[NitxMatch]
+ # Optional suggestion when no matches where found
+ var suggest: nullable Array[MEntity] = null is optional
+
redef fun render_title do
var len = results.length
if len == 0 then
- add "No result found for '{query.string}'..."
+ addn "No result found for '{query.string}'..."
+ var suggest = self.suggest
+ if suggest != null and suggest.not_empty then
+ add "\nDid you mean "
+ var i = 0
+ for s in suggest do
+ add "`{s.full_name}`"
+ if i == suggest.length - 2 then add ", "
+ if i == suggest.length - 1 then add " or "
+ i += 1
+ end
+ add "?"
+ end
else
add "# {len} result(s) for '{query.string}'".green.bold
end
import doc_commands
import doc_down
import doc_intros_redefs
+import model::model_index
# Generate content of `ReadmePage`.
#
context.pop
pop_buffer
end
+
+ # Find mentities matching `query`.
+ fun find_mentities(query: String): Array[MEntity] do
+ # search MEntities by full_name
+ var mentity = phase.doc.mentity_by_full_name(query)
+ if mentity != null then return [mentity]
+ # search MEntities by name
+ return phase.doc.mentities_by_name(query)
+ end
+
+ # Suggest mentities based on `query`.
+ fun suggest_mentities(query: String): Array[MEntity] do
+ return phase.doc.find(query, 3)
+ end
+
+ # Display a warning message with suggestions.
+ fun warn(token: TokenWikiLink, message: String, suggest: nullable Array[MEntity]) do
+ var msg = new Buffer
+ msg.append message
+ if suggest != null and suggest.not_empty then
+ msg.append " (suggestions: "
+ var i = 0
+ for s in suggest do
+ msg.append "`{s.full_name}`"
+ if i < suggest.length - 1 then msg.append ", "
+ i += 1
+ end
+ msg.append ")"
+ end
+ phase.warning(token.location, page, msg.write_to_string)
+ end
end
# MarkdownDecorator used to decorated the Readme file with links between doc entities.
var cmd = new DocCommand(link)
if cmd isa UnknownCommand then
# search MEntities by name
- var res = v.phase.doc.mentities_by_name(link.to_s)
+ var res = v.find_mentities(link.to_s)
# no match, print warning and display wikilink as is
if res.is_empty then
- v.phase.warning(token.location, v.page, "Link to unknown entity `{link}`")
+ v.warn(token, "Link to unknown entity `{link}`", v.suggest_mentities(link.to_s))
super
else
add_mentity_link(v, res.first, token.name, token.comment)
# Render the content of the doc command.
fun render(v: ReadmeMdEmitter, token: TokenWikiLink) is abstract
-
- # Search `doc` model for mentities match `string`.
- fun search_model(doc: DocModel, string: String): Array[MEntity] do
- var res
- if string.has("::") then
- res = doc.mentities_by_namespace(string).to_a
- else
- res = doc.mentities_by_name(string).to_a
- end
- return res
- end
end
redef class ArticleCommand
redef fun render(v, token) do
var string = args.first
- var res = search_model(v.phase.doc, string)
- res = filter_results(res)
+ var res = v.find_mentities(string)
if res.is_empty then
- v.phase.warning(
- token.location, v.page,
- "Try to include documentation of unknown entity `{args.first}`")
+ v.warn(token,
+ "Try to include documentation of unknown entity `{string}`",
+ v.suggest_mentities(string))
return
end
- if res.length > 1 then
- v.phase.warning(token.location, v.page, "conflicting article for `{args.first}` (choices : {res.join(", ")})")
- end
v.add_article new DocumentationArticle("readme", "Readme", res.first)
end
-
- private fun filter_results(res: Array[MEntity]): Array[MEntity] do
- var out = new Array[MEntity]
- for e in res do
- if e isa MPackage then continue
- if e isa MGroup then continue
- out.add e
- end
- return out
- end
end
redef class ListCommand
redef fun render(v, token) do
var string = args.first
- var res = search_model(v.phase.doc, string)
+ var res = v.find_mentities(string)
if res.is_empty then
- v.phase.warning(token.location, v.page, "include article for unknown entity `{args.first}`")
+ v.warn(token,
+ "Try to include article of unknown entity `{string}`",
+ v.suggest_mentities(string))
return
end
if res.length > 1 then
- v.phase.warning(token.location, v.page, "conflicting article for `{args.first}` (choices : {res.join(", ")})")
+ v.warn(token, "Conflicting article for `{args.first}`", res)
end
var mentity = res.first
if mentity isa MModule then
import model::model_views
import trees::trie
+redef class ModelView
+
+ # Keep a direct link to mentities by full name to speed up `mentity_from_uri`
+ var mentities_by_full_name: HashMap[String, MEntity] is lazy do
+ var mentities_by_full_name = new HashMap[String, MEntity]
+ for mentity in model.private_view.mentities do
+ mentities_by_full_name[mentity.full_name] = mentity
+ end
+ return mentities_by_full_name
+ end
+
+ # ModelIndex used to perform searches
+ var index: ModelIndex is lazy do
+ var index = new ModelIndex
+ for mentity in model.private_view.mentities do
+ if mentity isa MClassDef or mentity isa MPropDef then continue
+ index.index mentity
+ end
+ return index
+ end
+
+ # Find mentities by their `name`
+ fun mentities_by_name(name: String): Array[MEntity] do
+ if index.name_prefixes.has_key(name) then
+ return index.name_prefixes[name]
+ end
+ return new Array[MEntity]
+ end
+
+ redef fun mentity_by_full_name(full_name) do
+ if mentities_by_full_name.has_key(full_name) then
+ return mentities_by_full_name[full_name]
+ end
+ return null
+ end
+
+ private var score_sorter = new ScoreComparator
+ private var vis_sorter = new VisibilityComparator
+ private var kind_sorter = new MEntityComparator
+ private var name_sorter = new NameComparator
+ private var lname_sorter = new NameLengthComparator
+ private var fname_sorter = new FullNameComparator
+ private var lfname_sorter = new FullNameLengthComparator
+
+ # Search mentities based on a `query` string
+ #
+ # Lookup the view index for anything matching `query` and return `limit` results.
+ #
+ # The algorithm used is the following:
+ # 1- lookup by name prefix
+ # 2- lookup by full_name prefix
+ # 3- loopup by levenshtein distance
+ #
+ # At each step if the `limit` is reached, the algorithm stops and returns the results.
+ fun find(query: String, limit: nullable Int): Array[MEntity] do
+ # Find, lookup by name prefix
+ var matches = index.find_by_name_prefix(query).uniq.
+ sort(lname_sorter, name_sorter, kind_sorter)
+ if limit != null and matches.length >= limit then
+ return matches.limit(limit).rerank.sort(vis_sorter, score_sorter).mentities
+ end
+ matches = matches.rerank.sort(vis_sorter, score_sorter)
+
+ # If limit not reached, lookup by full_name prefix
+ var malus = matches.length
+ var full_matches = new IndexMatches
+ for match in index.find_by_full_name_prefix(query).
+ sort(kind_sorter, lfname_sorter, fname_sorter) do
+ match.score += malus
+ full_matches.add match
+ end
+ matches = matches.uniq
+ if limit != null and matches.length + full_matches.length >= limit then
+ matches.add_all full_matches
+ matches = matches.uniq.limit(limit).rerank.sort(vis_sorter, score_sorter)
+ return matches.mentities
+ end
+
+ # If limit not reached, lookup by similarity
+ malus = matches.length
+ var sim_matches = new IndexMatches
+ for match in index.find_by_similarity(query).sort(score_sorter, kind_sorter, lname_sorter, name_sorter) do
+ match.score += malus
+ sim_matches.add match
+ end
+ matches.add_all sim_matches
+ matches = matches.uniq
+ if limit != null then matches = matches.limit(limit)
+ return matches.rerank.sort(vis_sorter, score_sorter).mentities
+ end
+end
+
# ModelIndex indexes mentities by their name and full name
#
# It provides methods to find mentities based on a prefix or string similarity.
v.include_test_suite = self.include_test_suite
end
- # Searches MEntities that match `name`.
- fun mentities_by_name(name: String): Array[MEntity] do
- var res = new Array[MEntity]
- for mentity in mentities do if mentity.name == name then res.add mentity
- return res
- end
-
# Searches the MEntity that matches `full_name`.
fun mentity_by_full_name(full_name: String): nullable MEntity do
for mentity in mentities do
return null
end
- # Looks up a MEntity by its full `namespace`.
- #
- # Usefull when `mentities_by_name` returns conflicts.
- #
- # Namespaces must be of the form `package::core::module::Class::prop`.
- fun mentities_by_namespace(namespace: String): Array[MEntity] do
- var v = new LookupNamespaceVisitor(namespace)
- init_visitor(v)
- for mpackage in mpackages do
- v.enter_visit(mpackage)
- end
- return v.results
- end
-
# Build an concerns tree with from `self`
fun to_tree: MEntityTree do
var v = new ModelTreeVisitor
break
end
if p.mclassdef.mclass == mclassdef.mclass then
- # Still a warning to pass existing bad code
- modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.")
+ modelbuilder.error(n_type, "Redef Error: a virtual type cannot be refined.")
break
end
if not modelbuilder.check_subtype(n_type, mmodule, anchor, bound, supbound) then
intrude import doc_down
intrude import markdown::wikilinks
import doc_commands
+import model::model_index
redef class APIRouter
redef init do
write_error(v, "Not yet implemented command `{token.link or else "null"}`")
end
- # Find the MEntity ` with `full_name`.
- fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
- if full_name == null then return null
- return model.mentity_by_full_name(full_name.from_percent_encoding)
+ # Find the MEntity that matches `name`.
+ #
+ # Write an error if the entity is not found
+ fun find_mentity(v: MarkdownEmitter, model: ModelView, name: nullable String): nullable MEntity do
+ if name == null then
+ write_error(v, "No MEntity found")
+ return null
+ end
+ # Lookup by full name
+ var mentity = model.mentity_by_full_name(name)
+ if mentity != null then return mentity
+
+ var mentities = model.mentities_by_name(name)
+ if mentities.is_empty then
+ var suggest = model.find(name, 3)
+ var msg = new Buffer
+ msg.append "No MEntity found for name `{name}`"
+ if suggest.not_empty then
+ msg.append " (suggestions: "
+ var i = 0
+ for s in suggest do
+ msg.append "`{s.full_name}`"
+ if i < suggest.length - 1 then msg.append ", "
+ i += 1
+ end
+ msg.append ")"
+ end
+ write_error(v, msg.write_to_string)
+ return null
+ else if mentities.length > 1 then
+ var msg = new Buffer
+ msg.append "Conflicts for name `{name}`"
+ msg.append " (conflicts: "
+ var i = 0
+ for s in mentities do
+ msg.append "`{s.full_name}`"
+ if i < mentities.length - 1 then msg.append ", "
+ i += 1
+ end
+ msg.append ")"
+ write_warning(v, msg.write_to_string)
+ end
+ return mentities.first
end
# Write a warning in the output
return
end
var full_name = link.write_to_string
- var mentity = find_mentity(model, full_name)
- if mentity == null then
- write_error(v, "Unknown command `{link}`")
- return
- end
+ var mentity = find_mentity(v, model, full_name)
+ if mentity == null then return
write_mentity_link(v, mentity)
end
end
return
end
var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var mentity = find_mentity(v, model, name)
+ if mentity == null then return
var mdoc = mentity.mdoc_or_fallback
if mdoc == null then
write_warning(v, "No MDoc for mentity `{name}`")
return
end
var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var mentity = find_mentity(v, model, name)
+ if mentity == null then return
var mdoc = mentity.mdoc_or_fallback
if mdoc == null then
write_warning(v, "No MDoc for mentity `{name}`")
return
end
var name = args.first
- var mentity = find_mentity(model, name)
+ var mentity = find_mentity(v, model, name)
+ if mentity == null then return
if mentity isa MPackage then
write_list(v, mentity.mgroups)
else if mentity isa MGroup then
return
end
var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var mentity = find_mentity(v, model, name)
+ if mentity == null then return
if mentity isa MClass then mentity = mentity.intro
if mentity isa MProperty then mentity = mentity.intro
var source = render_source(mentity, v.decorator.as(NitwebDecorator).modelbuilder)
return
end
var name = args.first
- var mentity = find_mentity(model, name)
- if mentity == null then
- write_error(v, "No MEntity found for name `{name}`")
- return
- end
+ var mentity = find_mentity(v, model, name)
+ if mentity == null then return
var g = new InheritanceGraph(mentity, model)
v.add g.draw(3, 3).to_svg
end
end
var c = new B
-#alt1# c.e = new T
c.e = new U
c.e.foo
base_virtual_type4.nit:20,16: Redef Error: a virtual type cannot be refined.
-1
+++ /dev/null
-alt/base_virtual_type4_alt1.nit:20,16: Redef Error: a virtual type cannot be refined.
-alt/base_virtual_type4_alt1.nit:24,7--11: Type Error: expected `nullable U`, got `T`.
-\e[1m\e[32m# 2 result(s) for 'comment: A'\e[m\e[m
+\e[1m\e[34m# \e[m\e[m\e[1m\e[34mA\e[m\e[m
+\e[1m\e[30mclass A\e[m\e[m
+
+ \e[1m\e[32mclass A\e[m\e[m
+ \e[1m\e[30mbase_simple3.nit:29,1--32,3\e[m\e[m
- \e[1m\e[32mC\e[m\e[m \e[1m\e[34mA\e[m\e[m
- \e[1m\e[30mbase_simple3::A\e[m\e[m
- class A
- \e[30mbase_simple3.nit:29,1--32,3\e[m
- \e[1m\e[32mC\e[m\e[m \e[1m\e[34mA\e[m\e[m
- \e[1m\e[30mbase_simple3::base_simple3::A\e[m\e[m
- class A
- \e[30mbase_simple3.nit:29,1--32,3\e[m
-\e[1m\e[32m# 2 result(s) for 'comment: foo'\e[m\e[m
+\e[1m\e[34m# \e[m\e[m\e[1m\e[34mfoo\e[m\e[m
+\e[1m\e[30mfun foo\e[m\e[m
+
+ \e[1m\e[32mfun foo\e[m\e[m
+ \e[1m\e[30mbase_simple3.nit:49,1--19\e[m\e[m
- \e[1m\e[32mF\e[m\e[m \e[1m\e[34mfoo\e[m\e[m
- \e[1m\e[30mbase_simple3::Sys::foo\e[m\e[m
- fun foo
- \e[30mbase_simple3.nit:49,1--19\e[m
- \e[1m\e[32mF\e[m\e[m \e[1m\e[34mfoo\e[m\e[m
- \e[1m\e[30mbase_simple3::base_simple3::Sys::foo\e[m\e[m
- fun foo
- \e[30mbase_simple3.nit:49,1--19\e[m
-\e[1m\e[32m# 3 result(s) for 'comment: base_simple3'\e[m\e[m
+\e[1m\e[34m# \e[m\e[m\e[1m\e[34mbase_simple3\e[m\e[m
+\e[1m\e[30mpackage base_simple3\e[m\e[m
- \e[1m\e[32mP\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
- \e[1m\e[30mbase_simple3\e[m\e[m
- package base_simple3
- \e[30mbase_simple3.nit:17,1--66,13\e[m
+ \e[1m\e[32mpackage base_simple3\e[m\e[m
+ \e[1m\e[30mbase_simple3.nit:17,1--66,13\e[m\e[m
- \e[1m\e[32mG\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
- \e[1m\e[30mbase_simple3\e[m\e[m
- group base_simple3
- \e[30mbase_simple3.nit:17,1--66,13\e[m
- \e[1m\e[32mM\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
- \e[1m\e[30mbase_simple3::base_simple3\e[m\e[m
- module base_simple3
- \e[30mbase_simple3.nit:17,1--66,13\e[m