if _buffer_pos + i >= _buffer.length then
var from = _buffer_pos
_buffer_pos = _buffer.length
+ if from == 0 then return _buffer.to_s
return _buffer.substring_from(from).to_s
end
_buffer_pos += i
# String Specific Methods #
##################################################
- private init with_infos(items: NativeString, len: Int, from: Int, to: Int)
+ # Low-level creation of a new string with given data.
+ #
+ # `items` will be used as is, without copy, to retrieve the characters of the string.
+ # Aliasing issues is the responsibility of the caller.
+ private init with_infos(items: NativeString, length: Int, from: Int, to: Int)
do
self.items = items
- length = len
+ self.length = length
index_from = from
index_to = to
end
# Create a new empty string.
init do end
+ # Low-level creation a new buffer with given data.
+ #
+ # `items` will be used as is, without copy, to store the characters of the buffer.
+ # Aliasing issues is the responsibility of the caller.
+ #
+ # If `items` is shared, `written` should be set to true after the creation
+ # so that a modification will do a copy-on-write.
+ private init with_infos(items: NativeString, capacity, length: Int)
+ do
+ self.items = items
+ self.length = length
+ self.capacity = capacity
+ end
+
# Create a new string copied from `s`.
init from(s: Text)
do
init with_capacity(cap: Int)
do
assert cap >= 0
- # _items = new NativeString.calloc(cap)
items = new NativeString(cap+1)
capacity = cap
length = 0
if from < 0 then from = 0
if count > length then count = length
if from < count then
- var r = new FlatBuffer.with_capacity(count - from)
- while from < count do
- r.chars.push(items[from])
- from += 1
- end
+ var len = count - from
+ var r_items = new NativeString(len)
+ items.copy_to(r_items, len, from, 0)
+ var r = new FlatBuffer.with_infos(r_items, len, len)
return r
else
return new FlatBuffer
* Automatic indentation
* Syntax checker (require [Syntastic][2]).
* Autocomplete for whole projects using module importations
+ * Show documentation in preview window
+ * Search declarations and usages of the word under the cursor
[2]: https://github.com/scrooloose/syntastic
The metadata files from nitpick are stored in `~/.vim/nit/`. This location can be customized with
the environment variable `NIT_VIM_DIR`.
+
+## Documentation in preview window
+
+You can display the documentation for the entity under the cursor with `:call Nitdoc()`.
+It will use the same metadata files as the omnifunc and the preview window.
+You may want to map the function to a shortcut by adding the following code to `~/.vimrc`.
+
+~~~
+" Map displaying Nitdoc to Ctrl-D
+map <C-d> :call Nitdoc()<enter>
+~~~
+
+## Search declarations and usages of the word under the cursor
+
+The function `NitGitGrep` calls `git grep` to find declarations and usages of the word under the cursor.
+It displays the results in the preview window.
+You may want to map the function to a shortcut by adding the following code to `~/.vimrc`.
+
+~~~
+" Map the NitGitGrep function to Ctrl-G
+map <C-g> :call NitGitGrep()<enter>
+~~~
call NitComplete()
endfunction
-" Internal function to search for lines in `path` corresponding to the partial
-" word `base`. Adds found and formated match to `matches`.
+" Get path to the best metadata file named `name`
"
-" Will order the results in 3 levels:
-" 1. Exact matches
-" 2. Common prefix matches
-" 3. Substring matches
-fun NitOmnifuncAddFromFile(base, matches, path)
- let prefix_matches = []
- let substring_matches = []
-
+" Returns an empty string if not found.
+fun NitMetadataFile(name)
" Where are the generated metadata files?
if empty($NIT_VIM_DIR)
let metadata_dir = $HOME . '/.vim/nit'
let metadata_dir = $NIT_VIM_DIR
end
- let path = metadata_dir . '/' . a:path
+ let path = metadata_dir . '/' . a:name
+
" Is there generated custom metadata files?
if ! filereadable(path)
- let path = s:script_dir . '/' . a:path
+ let path = s:script_dir . '/' . a:name
" Is there standard metadata files?
if ! filereadable(path)
- return
+ return ''
endif
endif
+ return path
+endfun
+
+" Internal function to search for lines in `path` corresponding to the partial
+" word `base`. Adds found and formated match to `matches`.
+"
+" Will order the results in 5 levels:
+" 1. Exact matches
+" 2. Common prefix matches
+" 3. Substring matches
+" 4. Synopsis matches
+" 5. Doc matches
+fun NitOmnifuncAddFromFile(base, matches, path)
+ let prefix_matches = []
+ let substring_matches = []
+ let synopsis_matches = []
+ let doc_matches = []
+
+ let path = NitMetadataFile(a:path)
+ if empty(path)
+ return
+ endif
+
for line in readfile(path)
let words = split(line, '#====#', 1)
let name = get(words, 0, '')
if name == a:base
" Exact match
call NitOmnifuncAddAMatch(a:matches, words, name)
- elseif name =~ '^'.a:base
+ elseif name =~? '^'.a:base
" Common-prefix match
call NitOmnifuncAddAMatch(prefix_matches, words, name)
- elseif name =~ a:base
+ elseif name =~? a:base
" Substring match
call NitOmnifuncAddAMatch(substring_matches, words, name)
+ elseif get(words, 2, '') =~? a:base
+ " Match in the synopsis
+ call NitOmnifuncAddAMatch(synopsis_matches, words, name)
+ elseif get(words, 3, '') =~? a:base
+ " Match in the longer doc
+ call NitOmnifuncAddAMatch(synopsis_matches, words, name)
endif
endfor
" Assemble the final match list
call extend(a:matches, sort(prefix_matches))
call extend(a:matches, sort(substring_matches))
+ call extend(a:matches, sort(synopsis_matches))
+ call extend(a:matches, sort(doc_matches))
endfun
" Internal function to search parse the information from a metadata line
" find keyword matching with "a:base"
let matches = []
- " Advanced suggestions
+ " advanced suggestions
let cursor_line = getline('.')
- " Content of the line before the partial word
+ " content of the line before the partial word
let line_prev_cursor = cursor_line[:col('.')-1]
let prev_char_at = strlen(line_prev_cursor) - 1
endif
endfun
+" Show doc for the entity under the cursor in the preview window
+fun Nitdoc()
+ " Word under cursor
+ let word = expand("<cword>")
+
+ " All possible docs (there may be more than one entity with the same name)
+ let docs = []
+
+ " Search in all metadata files
+ for file in ['modules', 'classes', 'properties']
+ let path = NitMetadataFile(file.'.txt')
+ if empty(path)
+ continue
+ endif
+
+ for line in readfile(path)
+ let words = split(line, '#====#', 1)
+ let name = get(words, 0, '')
+ if name =~ '^' . word
+ " It fits our word, get long doc
+ let desc = get(words,3,'')
+ let desc = join(split(desc, '#nnnn#', 1), "\n")
+ call add(docs, desc)
+ endif
+ endfor
+ endfor
+
+ " Found no doc, give up
+ if empty(docs) || !(join(docs, '') =~ '\w')
+ return
+ endif
+
+ " Open the preview window on a temp file
+ execute "silent pedit " . tempname()
+
+ " Change to preview window
+ wincmd P
+
+ " Show all found doc one after another
+ for doc in docs
+ if doc =~ '\w'
+ silent put = doc
+ silent put = ''
+ endif
+ endfor
+
+ " Set options
+ setlocal buftype=nofile
+ setlocal noswapfile
+ setlocal syntax=none
+ setlocal bufhidden=delete
+
+ " Change back to the source buffer
+ wincmd p
+ redraw!
+endfun
+
+" Call `git grep` on the word under the cursor
+"
+" Shows declarations first, then all matches, in the preview window.
+fun NitGitGrep()
+ let word = expand("<cword>")
+ let out = tempname()
+ execute 'silent !(git grep "\\(module\\|class\\|universal\\|interface\\|var\\|fun\\) '.word.'";'.
+ \'echo; git grep '.word.') > '.out
+
+ " Open the preview window on a temp file
+ execute "silent pedit " . out
+
+ " Change to preview window
+ wincmd P
+
+ " Set options
+ setlocal buftype=nofile
+ setlocal noswapfile
+ setlocal syntax=none
+ setlocal bufhidden=delete
+
+ " Change back to the source buffer
+ wincmd p
+ redraw!
+endfun
+
" Activate the omnifunc on Nit files
autocmd FileType nit set omnifunc=NitOmnifunc
var modelbuilder = v.compiler.modelbuilder
var val = constant_value
var node = modelbuilder.mpropdef2node(self)
+
+ if is_abstract then
+ var cn = v.class_name_string(arguments.first)
+ v.current_node = node
+ v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
+ v.add_raw_abort
+ return null
+ end
+
if node isa APropdef then
var oldnode = v.current_node
v.current_node = node
redef class AMethPropdef
redef fun compile_to_c(v, mpropdef, arguments)
do
- if mpropdef.is_abstract then
- var cn = v.class_name_string(arguments.first)
- v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
- v.add_raw_abort
- return
- end
-
# Call the implicit super-init
var auto_super_inits = self.auto_super_inits
if auto_super_inits != null then
# 4. Full doc with extra
stream.write field_separator
+ stream.write "# "
+ stream.write full_name
+ write_signature_to_stream(stream)
if mdoc != null then
- stream.write "# "
- stream.write full_name
- write_signature_to_stream(stream)
for i in 2.times do stream.write line_separator
stream.write mdoc.content.join(line_separator)
end
end
# Add a constructor to the automated nclassdef
- fun generate_deserialization_init(nclassdef: AClassdef)
+ fun generate_deserialization_init(nclassdef: AStdClassdef)
do
+ # Do not generate constructors for abstract classes
+ if nclassdef.n_classkind isa AAbstractClasskind then return
+
var npropdefs = nclassdef.n_propdefs
var code = new Array[String]
for nclassdef in nclassdefs do
var name = nclassdef.n_id.text
- if nclassdef.n_formaldefs.is_empty then
+ if nclassdef.n_formaldefs.is_empty and
+ not nclassdef.n_classkind isa AAbstractClasskind then
+
code.add " if name == \"{name}\" then return new {name}.from_deserializer(self)"
end
end
var val = mpropdef.constant_value
var node = modelbuilder.mpropdef2node(mpropdef)
+ if mpropdef.is_abstract then
+ if node != null then
+ self.frames.unshift new Frame(node, mpropdef, args)
+ end
+ fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
+ abort
+ end
+
if node isa APropdef then
self.parameter_check(node, mpropdef, args)
return node.call(self, mpropdef, args)
v.write_variable(variable, arguments[i+1])
end
- if mpropdef.is_abstract then
- v.fatal("Abstract method `{mpropdef.mproperty.name}` called on `{arguments.first.mtype}`")
- abort
- end
-
# Call the implicit super-init
var auto_super_inits = self.auto_super_inits
if auto_super_inits != null then
evaluate_expr(v, recv)
return
end
- var mtype = self.mpropdef.static_mtype.as(not null)
+ var mpropdef = self.mpropdef
+ if mpropdef == null then return
+ var mtype = mpropdef.static_mtype.as(not null)
mtype = mtype.anchor_to(v.mainmodule, recv.mtype.as(MClassType))
if mtype isa MNullableType then
v.write_attribute(self.mpropdef.mproperty, recv, v.null_instance)
redef fun build_property(modelbuilder, mclassdef)
do
var mclass = mclassdef.mclass
+ var nid2 = n_id2
+ var name = nid2.text
+
+ var atabstract = self.get_single_annotation("abstract", modelbuilder)
+ if atabstract == null then
+ if mclass.kind == interface_kind then
+ modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
+ else if mclass.kind == enum_kind then
+ modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
+ else if mclass.kind == extern_kind then
+ modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
+ end
- var name: String
- name = self.n_id2.text
-
- if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then
- modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.")
- else if mclass.kind == enum_kind then
- modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.")
- else if mclass.kind == extern_kind then
- modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.")
+ var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
+ var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
+ self.mpropdef = mpropdef
+ modelbuilder.mpropdef2npropdef[mpropdef] = self
end
- # New attribute style
- var nid2 = self.n_id2
- var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
- var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
- self.mpropdef = mpropdef
- modelbuilder.mpropdef2npropdef[mpropdef] = self
-
var readname = name
var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
if mreadprop == null then
var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
mreadprop = new MMethod(mclassdef, readname, mvisibility)
if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
- mreadprop.deprecation = mprop.deprecation
else
if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
self.mreadpropdef = mreadpropdef
modelbuilder.mpropdef2npropdef[mreadpropdef] = self
set_doc(mreadpropdef, modelbuilder)
- mpropdef.mdoc = mreadpropdef.mdoc
+ if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc
+ if atabstract != null then mreadpropdef.is_abstract = true
has_value = n_expr != null or n_block != null
+ if atabstract != null and has_value then
+ modelbuilder.error(atabstract, "Error: `abstract` attributes cannot have an initial value")
+ return
+ end
+
var atnoinit = self.get_single_annotation("noinit", modelbuilder)
if atnoinit == null then atnoinit = self.get_single_annotation("noautoinit", modelbuilder)
if atnoinit != null then
modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value")
return
end
+ if atabstract != null then
+ modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract")
+ return
+ end
end
var atlazy = self.get_single_annotation("lazy", modelbuilder)
end
mwriteprop = new MMethod(mclassdef, writename, mvisibility)
if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
- mwriteprop.deprecation = mprop.deprecation
+ mwriteprop.deprecation = mreadprop.deprecation
else
if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
if atwritable != null then
var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
self.mwritepropdef = mwritepropdef
modelbuilder.mpropdef2npropdef[mwritepropdef] = self
- mwritepropdef.mdoc = mpropdef.mdoc
+ mwritepropdef.mdoc = mreadpropdef.mdoc
+ if atabstract != null then mwritepropdef.is_abstract = true
end
redef fun build_signature(modelbuilder)
do
+ var mreadpropdef = self.mreadpropdef
var mpropdef = self.mpropdef
- if mpropdef == null then return # Error thus skipped
- var mclassdef = mpropdef.mclassdef
+ if mreadpropdef == null then return # Error thus skipped
+ var mclassdef = mreadpropdef.mclassdef
var mmodule = mclassdef.mmodule
var mtype: nullable MType = null
- var mreadpropdef = self.mreadpropdef
var ntype = self.n_type
if ntype != null then
var inherited_type: nullable MType = null
# Inherit the type from the getter (usually an abstract getter)
- if mreadpropdef != null and not mreadpropdef.is_intro then
+ if not mreadpropdef.is_intro then
var msignature = mreadpropdef.mproperty.intro.msignature
if msignature == null then return # Error, thus skipped
inherited_type = msignature.return_mtype
var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
if cla != null then mtype = cla.mclass_type
else
- modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.")
+ modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}. Implicit typing allowed only for literals and new.")
end
if mtype == null then return
end
if mtype == null then
- modelbuilder.error(self, "Error: Untyped attribute {mpropdef}")
+ modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}")
return
end
- mpropdef.static_mtype = mtype
+ if mpropdef != null then
+ mpropdef.static_mtype = mtype
+ end
- if mreadpropdef != null then
+ do
var msignature = new MSignature(new Array[MParameter], mtype)
mreadpropdef.msignature = msignature
end
# Where do we put the result?
var opt_dir: OptionString = new OptionString("Output directory", "--dir")
+ # Depth of the visit and generation
+ var opt_depth = new OptionEnum(["module", "group", "project"],
+ "Depth of the visit and generation", 0, "-d", "--depth")
+
redef init
do
- option_context.add_option(opt_output, opt_dir)
+ option_context.add_option(opt_output, opt_dir, opt_depth)
super
end
end
var mmodules = modelbuilder.parse_full(arguments)
modelbuilder.run_phases
-# Create a distinct support module per targetted modules
+# Create a distinct support module per target modules
for mmodule in mmodules do
- var rta = modelbuilder.do_rapid_type_analysis(mmodule)
-
# Name of the support module
var module_name
module_path += ".nit"
end
+ var target_modules = null
+ var importations = null
+ var mgroup = mmodule.mgroup
+ if toolcontext.opt_depth.value == 1 and mgroup != null then
+ modelbuilder.visit_group mgroup
+ target_modules = mgroup.mmodules
+ else if toolcontext.opt_depth.value == 2 then
+ # project
+ target_modules = new Array[MModule]
+ importations = new Array[MModule]
+ if mgroup != null then
+ for g in mgroup.mproject.mgroups do
+ target_modules.add_all g.mmodules
+ end
+
+ for g in mgroup.in_nesting.direct_smallers do
+ var dm = g.default_mmodule
+ if dm != null then
+ importations.add dm
+ end
+ end
+
+ for m in mgroup.mmodules do
+ importations.add m
+ end
+ end
+ end
+
+ if target_modules == null then target_modules = [mmodule]
+ if importations == null then importations = target_modules
+
var nit_module = new NitModule(module_name)
nit_module.header = """
# This file is generated by nitserial
# Do not modify, but you can redef
"""
- nit_module.imports.add mmodule.name
+ for importation in importations do
+ nit_module.imports.add importation.name
+ end
+
nit_module.imports.add "serialization"
nit_module.content.add """
do"""
var serializable_type = mmodule.serializable_type
- for mtype in rta.live_types do
- # We are only interested in instanciated generics, subtypes of Serializable
- # and which are visibles.
- if mtype isa MGenericType and
- mtype.is_subtype(mmodule, null, serializable_type) and
- mtype.is_visible_from(mmodule) then
-
- nit_module.content.add """
+ var compiled_types = new Array[MType]
+ for m in target_modules do
+ nit_module.content.add """
+ # Module: {{{m.to_s}}}"""
+
+ var rta = modelbuilder.do_rapid_type_analysis(m)
+
+ for mtype in rta.live_types do
+ # We are only interested in instanciated generics, subtypes of Serializable
+ # and which are visibles.
+ if mtype isa MGenericType and
+ mtype.is_subtype(m, null, serializable_type) and
+ mtype.is_visible_from(mmodule) and
+ not compiled_types.has(mtype) then
+
+ compiled_types.add mtype
+ nit_module.content.add """
if name == \"{{{mtype}}}\" then return new {{{mtype}}}.from_deserializer(self)"""
+ end
end
end
if mproperty.mpropdefs.length <= 1 then return
# If all definitions of a method are live, we can remove the definition of the totry set
for d in mproperty.mpropdefs do
- if d.is_abstract then continue
if not live_methoddefs.has(d) then return
end
#print "full property: {mpropdef.mproperty} for {mpropdef.mproperty.mpropdefs.length} definitions"
redef class AAttrPropdef
redef fun do_typing(modelbuilder: ModelBuilder)
do
+ if not has_value then return
+
var mpropdef = self.mpropdef.as(not null)
var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
self.selfvariable = v.selfvariable
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+interface Foo
+ var a: Object is abstract
+ #alt1#var b = 1 is abstract
+ #alt2#var b is abstract, noautoinit
+end
+
+class Bar
+ super Foo
+ redef var a
+end
+
+class Baz
+ super Foo
+ redef fun a do return 100
+ redef fun a=(x) do (101).output
+end
+
+class FooBar
+ super Foo
+end
+
+var f: Foo = new Bar(1)
+f.a.output
+f.a = 2
+f.a.output
+
+f = new Baz
+f.a.output
+f.a = 3
+f.a.output
+
+f = new FooBar
+#alt3#f.a.output
+#alt4#f.a = 4
alt/base_attr5_alt17.nit:47,12--14: Error: No property B::bar is inherited. Remove the redef keyword to define a new property.
-alt/base_attr5_alt17.nit:47,12--14: Error: Untyped attribute base_attr5_alt17#B#_bar
--- /dev/null
+1
+2
+100
+101
+100
--- /dev/null
+alt/base_attr_abstract_alt1.nit:19,15--22: Error: `abstract` attributes cannot have an initial value
--- /dev/null
+alt/base_attr_abstract_alt2.nit:20,6: Error: Untyped attribute base_attr_abstract_alt2#Foo#b
+alt/base_attr_abstract_alt2.nit:20,21--30: Error: `noautoinit` attributes cannot be abstract
--- /dev/null
+Runtime error: Abstract method `a` called on `FooBar` (alt/base_attr_abstract_alt3.nit:18)
+1
+2
+100
+101
+100
--- /dev/null
+Runtime error: Abstract method `a=` called on `FooBar` (alt/base_attr_abstract_alt4.nit:18)
+1
+2
+100
+101
+100
-error_kern_attr_int.nit:18,6--9: Error: Attempt to define attribute toto in the interface Int.
+error_kern_attr_int.nit:18,6--9: Error: Attempt to define attribute toto in the enum class Int.
redef class Deserializer
redef fun deserialize_class(name)
do
+ # Module: test_serialization
if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)